13134 Commits
Author SHA1 Message Date
Antje WorringandClaude Opus 4.8 4a6c872ae3 docs: tidy LLM.md indexes; CLAUDE.md -> LLM.md symlink convention
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 09:59:49 -07:00
Hanzo DevandGitHub c4d7b32653 Merge branch 'redis:unstable' into unstable 2026-06-03 12:52:56 -07:00
Sergei GeorgievandGitHub 1d6888b20d Validate stream listpack first entry on RDB/RESTORE load (#15295)
**Summary**

The stream RDB/RESTORE loader read the first element of each stream
listpack node (the "valid entries" count) and immediately decoded it as
an integer, but under shallow validation (`sanitize-dump-payload no`)
only the listpack header had been checked — never the first entry
itself. A crafted payload whose first entry declares an oversized string
encoding (e.g. `LP_ENCODING_32BIT_STR` claiming `0x7FFFFFFF` bytes)
caused `lpGetIntegerValue()` to read encoding-dependent bytes past the
end of the listpack, triggering an out-of-bounds read / crash.

**Changes**

1. **First-entry validation before integer decode (`rdb.c`)**
In `rdbLoadObject`, the stream-loading path now obtains the first
element via `lpValidateFirst()` instead of `lpFirst()`, and then
validates that entry with `lpValidateNext()` before
`lpGetIntegerValue()` decodes it. If the entry is malformed, the load is
rejected cleanly via `rdbReportCorruptRDB("Stream listpack integrity
check failed.")` with proper cleanup (`sdsfree(nodekey)`,
`decrRefCount(o)`, `zfree(lp)`) and an early `NULL` return, matching how
other corrupt-payload cases are handled. This closes the gap where only
the listpack header — not its first entry — was checked under
`sanitize-dump-payload no`.

2. **Test (`corrupt-dump.tcl`)**
A new test (`corrupt payload: stream listpack entry with corrupt
encoding crashes lpFirst`) exercises the rejection path. It disables
payload sanitization and checksum validation, then issues a `RESTORE`
with a hand-built stream payload whose listpack has a valid header but a
first entry encoded as `LP_ENCODING_32BIT_STR` (`0xF0`) declaring a
`0x7FFFFFFF`-byte string that runs past the end of the listpack. The
test asserts the command fails with `*Bad data format*`, verifies the
`*Stream listpack integrity check failed*` warning is logged, and
confirms the server survives (`r ping`).
2026-06-03 10:03:25 +03:00
c0a83262a3 Fix integer overflow in cluster bus PUBLISH and MODULE message length validation (#15187)
## Summary

Add overflow checks for attacker-controlled `uint32_t` length fields in
`clusterProcessPacket()` before adding them to `explen` (`uint32_t`).
Without these checks, crafted PUBLISH/PUBLISHSHARD and MODULE cluster
bus messages can wrap `explen` to a small value, bypassing the `totlen
!= explen` validation and causing heap out-of-bounds reads or OOM aborts
in the processing path.

## Problem

In `clusterProcessPacket()`, the expected packet length for PUBLISH and
MODULE messages is computed by summing struct overhead with
variable-length fields read from the packet header via `ntohl()`:

```c
// PUBLISH (line 2841-2846)
explen += sizeof(clusterMsgDataPublish) - 8 +
    ntohl(hdr->data.publish.msg.channel_len) +
    ntohl(hdr->data.publish.msg.message_len);

// MODULE (line 2855-2858)
explen += sizeof(clusterMsgModule) - 3 + ntohl(hdr->data.module.msg.len);
```

Both `channel_len + message_len` (PUBLISH) and `len` (MODULE) are
`uint32_t` values from the network packet. Their addition to `explen`
can overflow `uint32_t`, wrapping to a small value that matches
`totlen`, passing the size validation at line 2864.

**Example (PUBLISH):** With `channel_len = 0x80000000` and `message_len
= 0x80000000`, their sum overflows to `0`, making `explen` equal to just
the base struct size. An attacker sets `totlen` to match. The validation
passes, and the processing path at line 3273 calls
`createStringObject()` with the original 2GB lengths, reading far past
the received buffer.

**Attack vector:** Reachable via the cluster bus port (default: data
port + 10000), which does not require authentication by default.

## Fix

Extract `ntohl` values into local variables and check each addition
against `UINT32_MAX` before performing it. Reject the packet with a
warning log if overflow is detected. The computed `explen` is identical
to the original for all non-overflowing (legitimate) inputs.

---------

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Co-authored-by: debing.sun <debing.sun@redis.com>
2026-06-03 12:09:49 +08:00
Hanzo AI b46813a86f merge: upstream/unstable (preserve Hanzo brand, 117 commits) 2026-06-02 10:00:55 -07:00
2df35f9fb0 Avoid double-walk on find-then-insert via raxFindLink + raxInsertAt (#15252)
Add `raxFindLink()` + `raxInsertAt()`: a two-step commit API that
lets a caller walk the rax once to find a key, then commit an insert
at the recorded position without re-walking. Today's
"lookup then insert" pattern (`raxFind` + `raxInsert` on miss) walks
the tree twice; this change collapses the worst case to a single
walk.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: debing.sun <debing.sun@redis.com>
2026-06-02 18:01:16 +03:00
GuyAv46andGitHub 1b3721eb22 Clarify RedisModule_GetClusterNodeInfo buffer sizes (#15268)
Updates the in-source documentation for RedisModule_GetClusterNodeInfo
so module authors size caller buffers correctly.
2026-06-02 17:24:44 +08:00
Vitah LinandGitHub 148de48334 Fix potential TCP deadlock in module datatype defrag test (#15274)
### Issue
The module datatype defrag test sends 20k commands through a deferred
client before reading any replies. On slower CI environments this can
cause replies to accumulate and fill TCP/socket buffers, leading to
flaky `I/O error reading reply` failures.

### Change

Fix by batching deferred writes and reply drains, following the same
approach used in #14886.
2026-06-02 09:37:29 +08:00
nuybandGitHub 09b5c95b98 Fix AUTH failure on URIs with empty username in redis-cli (#15255)
Partial fix #11085

## Bug

`parseRedisUri()` called `percentDecode(curr, 0)` for URIs of the form
`redis://:password@host` and stored the resulting **empty-but-non-NULL**
sds into `connInfo->user`.
`cliAuth()` only checks `user == NULL` when deciding between legacy
`AUTH <pass>` and ACL `AUTH <user> <pass>`, so the empty username was
sent as `AUTH "" <password>`, which the server rejects with `WRONGPASS`.

## Fix

In parseRedisUri(), treat explicitly empty username or password
components as NULL rather than empty SDS strings. This allows cliAuth()
to fall back to legacy single-argument AUTH when the username is empty,
and to skip AUTH entirely when the password is empty.

Before replacing URI-parsed credentials, existing connInfo->user and
connInfo->auth values are freed to avoid leaks and preserve the expected
"later arguments override earlier ones" behavior.

As a related cleanup, -a/--pass and --user now also free previously
assigned values before reassignment, fixing the same leak pattern when
those options are specified multiple times.
2026-06-02 09:29:56 +08:00
Hanzo AI 9cc798fa10 merge: ci/canonical-docker-build-1776995843 (-X theirs) 2026-06-01 18:10:36 -07:00
Cong ChenandGitHub 230c651c89 tests:remove flaky SAMPLE 1000 case from hotkeys biased-access test (#15285)
Fixes: test faulure in
https://github.com/redis/redis/actions/runs/26698983853/job/78688240089

While investigating the failure, I added temporary debug output:

```tcl
puts "cpu_time_array=$cpu_time_array"
puts "num_returned_cpu=$num_returned_cpu"
puts "res=$res"
```

The failing SAMPLE 1000 run produced:

```
cpu_time_array=key_010 3 key_000 3 key_015 2 key_005 2 key_013 2 key_056 2 key_054 2 key_046 1 key_044 1 key_049 1
num_returned_cpu=10
res=5
```

The test becomes statistically unstable at SAMPLE 1000. With only ~50
sampled operations out of 50k requests, the accumulated CPU times
collapse into a very narrow range (observed: 1–3 µs), causing frequent
ties between hot and cold keys near the Top-K cutoff. The resulting
failures are driven by sampling variance rather than HOTKEYS
correctness.

Since the test already covers sampling behavior with ratios 1, 100, and
500, removing the 1000 case preserves coverage while eliminating a
configuration that is too sparse to produce stable, reproducible
assertions.
2026-06-01 17:05:36 +03:00
nuybandGitHub 7f532eacdf Fix garbled "source node not found" error in atomic slot migration (#15284)
asmSyncWithSource() built the error with sdscatfmt() and a "%.40s"
specifier. sdscatfmt() is not printf: it parses only the single byte
after '%' and ignores width/precision, so "%.40s" emits a literal '.',
consumes no argument, and task->source is never printed. The message
rendered as "Source node .40s was not found", dropping the node name.

task->source is a CLUSTER_NAMELEN (40) byte, non-NUL-terminated buffer
(filled via memcpy and always read with an explicit length elsewhere),
so simply switching to sdscatfmt's "%s" would strlen() past the buffer.
Use sdscatprintf(), which honors the "%.40s" precision and bounds the
read to 40 bytes -- matching the sibling error paths in this function
that already use sdscatprintf().
2026-06-01 21:27:35 +08:00
Vitah LinandGitHub 8fcf3dc866 Fix vector set tests to use RESP2 for default clients (#15287)
## Issue

The vector set Python tests intentionally use two clients: 
- the default client (`self.redis`) for the existing RESP2-oriented test
expectations
- `self.redis3` for RESP3-specific coverage.

However, the default client did not explicitly set a protocol, so it
depended on redis-py's default behavior. With newer redis-py versions,
RESP3 is now the default
protocol(https://github.com/redis/redis-py/pull/4052). In particular,
vector set replies such as `VSIM ... WITHSCORES` may be parsed into
map/dict-like structures instead of the RESP2 flat-array shape assumed
by existing tests.

## Changes

Explicitly create the default primary and replica Redis clients with
`protocol=2`.
`self.redis3` is left unchanged and continues to use `protocol=3` for
RESP3-specific test coverage.
2026-06-01 13:31:20 +08:00
Hanzo DevandGitHub 789b1b7b95 Merge pull request #1 from hanzoai/upstream-sync-20260327
upstream: merge redis/redis unstable (58 commits)
2026-05-29 21:08:32 -07:00
65401042dc Offload length formatting in tryAvoidBulkStrCopyToReply() to IO thread (#14844)
## Summary

In the reply copy-avoidance path (bulkStrRef), the RESP bulk-string
prefix `$<len>\r\n` was formatted eagerly on the main thread inside
`_addBulkStrRefToBufferOrList()`. This PR defers that formatting to
write time via a new idempotent helper `formatBulkStrRefPrefix()`, so
the work happens on the IO thread for clients served by IO threads.

---------

Co-authored-by: Yuan Wang <yuan.wang@redis.com>
2026-05-29 11:01:10 +08:00
Vitah LinandGitHub a36cfc0829 Fix flaky slot-stats inline network-bytes-in test (#15279)
### Issue

CLUSTER SLOT-STATS network-bytes-in, in-line buffer processing can fail
because the test sends an inline SET through a deferring client and
immediately checks slot stats from another client.

$rd flush only guarantees the request was written to the socket; it does
not guarantee Redis has processed the command and updated
network-bytes-in.

### Change

Wait for the inline SET reply before reading CLUSTER SLOT-STATS.
2026-05-29 09:57:59 +08:00
30f57f32bd Optimize redis-cli interactive output waiting (#15264)
### Issue

This refines the fix from #15119.

The previous change increased the generic `read_cli` empty-read retry
threshold from 5 to 100 to reduce flakiness in the redis-cli reverse
search no-result test. While effective, that made every interactive CLI
read potentially wait longer.

### Changes

This restores the original generic `read_cli` behavior and uses targeted
pattern-based waiting only where needed.

---------

Co-authored-by: debing.sun <debing.sun@redis.com>
2026-05-28 09:20:15 +08:00
h.o.t. neglectedandGitHub 138263a1b4 Fix cache line false sharing on per-IO-thread stat counters (#14907)
`stat_io_reads_processed[]` and `stat_io_writes_processed[]` were
per-IO-thread arrays inside `struct redisServer` that suffer from false
sharing. This PR moves the two stat counters into the IOThread struct,
which is already `__attribute__((aligned(CACHE_LINE_SIZE)))`. Each IO
thread's counters now sit on a separate cache line, eliminating the
cross-thread contention.
- Added io_reads_processed and io_writes_processed fields to IOThread struct
- Removed stat_io_reads_processed[] and stat_io_writes_processed[] from struct redisServer
- Made IOThreads[] non-static with extern declaration in server.h
2026-05-26 11:55:38 +08:00
f023cf026b Adjust fastfloat tests for libc-dependent nan payload parsing (#15110)
## Problem

`strtod()` handles some `nan(n-char-sequence)` inputs differently across
libc implementations. For example, `nan(ab!c)` and `nan(ab c)` may be
accepted on some platforms but rejected on others.

The existing test treated these inputs as fixed invalid cases, which can
fail on platforms whose libc accepts them.

## Changes

- Move libc-dependent `nan(...)` cases out of the fixed invalid test
list.
- Add a helper to verify `fast_float_strtod()` matches the platform
`strtod()` behavior for these cases, including value, `endptr`, and
success/failure status.
- Keep the existing parser behavior unchanged.

---------

Co-authored-by: debing.sun <debing.sun@redis.com>
2026-05-26 09:35:01 +08:00
fa08257fcb Fix flaky redis-cli reverse search no-result test (#15119)
## Problem

The `redis-cli` reverse-search test for the no-result case can be flaky
in slower CI environments.

`read_cli` may return too early when CLI output is fragmented or
delayed. It currently gives up after only 5 consecutive empty reads,
with a 10ms delay between reads, which can make the test assert before
the expected `(empty array)` output is printed.

## Changes

Increase the `read_cli` consecutive empty-read threshold from `5` to
`100`.

This keeps the existing read behavior unchanged when data is available,
but allows the helper to wait longer for delayed/fragmented CLI output
before giving up.

---------

Co-authored-by: debing.sun <debing.sun@redis.com>
2026-05-26 08:57:13 +08:00
13d9553673 Add concurrency groups to cancel stale GH workflow runs (#15224)
This is based on
[valkey-io/valkey#849](https://github.com/valkey-io/valkey/pull/849)

Introduce `concurrency` and `group` keywords into workflows execpt from
`redis_docs_sync` (which is triggered on release event only) and
`daily`.

https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency

With this, only one workflow can run in a group at any time.

---------

Co-authored-by: Yury-Fridlyand <yury.fridlyand@improving.com>
2026-05-26 08:54:39 +08:00
dariaguyandGitHub 8757b125b2 Remove handle-werrors target from modules/Makefile (#15257) 2026-05-25 23:18:16 +03:00
1587755a22 Add RM_GetClusterNodeSlotRanges module API (#14953)
Add new module API `RM_GetClusterNodeSlotRanges` that allows modules
to query slot ranges for any cluster node by its node ID, not just the
local node:

```c
RedisModuleSlotRangeArray *RM_GetClusterNodeSlotRanges(RedisModuleCtx *ctx, const char *nodeid)
```

---------

Co-authored-by: Ozan Tezcan <ozantezcan@gmail.com>
Co-authored-by: Yuan Wang <yuan.wang@redis.com>
2026-05-25 22:56:44 +08:00
Vitah LinandGitHub 368786ead5 Update GH Actions dependency (Node.js 20 deprecation) (#15216)
This is a follow-up to
[redis/redis#14938](https://github.com/redis/redis/pull/14938), which
upgraded GitHub Actions to newer stable versions for the upcoming
Node.js 20 deprecation on GitHub Actions runners.

That PR missed two remaining action updates in `daily.yml`:

- `cross-platform-actions/action`
- `py-actions/py-dependency-install`

### Why replace `py-actions/py-dependency-install`

`py-actions/py-dependency-install` is no longer an actively maintained
dependency installation action, so keeping it in CI increases
maintenance and supply-chain risk over time.

The replacement uses GitHub's official `actions/setup-python` action,
which is actively maintained and supports built-in `pip` dependency
caching. Installing dependencies with `python -m pip install -r
./utils/req-res-validator/requirements.txt` also makes the workflow
behavior explicit and easier to debug.
2026-05-25 19:20:09 +08:00
f9b7fa0a96 Update DataType Modules to v8.8.0 (#15238)
## Summary

Bumps `MODULE_VERSION` for RedisBloom, RedisJSON, and RedisTimeSeries
from `v8.7.91` to `v8.8.0`.

| Module          | From    | To     |
|-----------------|---------|--------|
| RedisBloom      | v8.7.91 | v8.8.0 |
| RedisJSON       | v8.7.91 | v8.8.0 |
| RedisTimeSeries | v8.7.91 | v8.8.0 |

## Module changes (v8.7.91 → v8.8.0)

### RedisBloom
([v8.7.91...v8.8.0](https://github.com/RedisBloom/RedisBloom/compare/v8.7.91...v8.8.0))
- MOD-15418 — fix load rdb mem leak
([#1007](https://github.com/RedisBloom/RedisBloom/pull/1007))
- Revert "fix redis version"
([#1010](https://github.com/RedisBloom/RedisBloom/pull/1010))
- bump version to v8.8.0

### RedisJSON
([v8.7.91...v8.8.0](https://github.com/RedisJSON/RedisJSON/compare/v8.7.91...v8.8.0))
- Revert "fix redis version"
([#1597](https://github.com/RedisJSON/RedisJSON/pull/1597))
- bump version v8.8.0

### RedisTimeSeries
([v8.7.91...v8.8.0](https://github.com/RedisTimeSeries/RedisTimeSeries/compare/v8.7.91...v8.8.0))
- MOD-14439 — Detect cluster topology changes during a multi-shard
command and return an appropriate error
([#1930](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1930))
- Revert Docker and CI redis-ref from 8.8 back to unstable
([#2033](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2033))
- bump version v8.8.0

## Test plan
- [ ] CI passes for all three modules at the new pinned version
- [ ] \`make all\` builds RedisBloom, RedisJSON, RedisTimeSeries cleanly
against \`unstable\`
- [ ] Module tests run under \`make test\`

🤖 Generated with [Claude Code](https://claude.com/claude-code)



Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:52:41 +03:00
Omer ShadmiandGitHub ba1a4b2c8f Update RediSearch module to v8.8.0 (#15228)
Updates the bundled RediSearch module version used by the Redis 8.8
branch from `v8.7.91` to `v8.8.0`.
2026-05-20 15:45:06 +03:00
Vitah LinandGitHub 457a1a542d Test fix "too many open files" on macOS (#14853)
On macOS, running `make test` often fails with "too many open files" due
to the low default limit (usually 256).

This PR increases the limit by adding `ulimit -n 4096` so that the tests
have enough file descriptors for concurrent connections.
2026-05-20 19:01:45 +08:00
95040d61d5 Replace INCREX out-of-bounds policy to a single SATURATE option (#15237)
Follow https://github.com/redis/redis/issues/15045

## Summary

Simplify INCREX's out-of-bounds policy:

The original INCREX shipped with three out-of-bounds policies — OVERFLOW
FAIL, OVERFLOW SAT, OVERFLOW REJECT — but FAIL and REJECT are
functionally redundant: both leave the key untouched when the result is
out of bounds. They differ only in how the caller is notified (error
reply vs. [current_value, 0] array reply), which forces the user to make
a stylistic choice with no real semantic difference.

This PR collapses the three policies into one clear behavior:

* Default: the operation is rejected; the key value and TTL are left
unchanged, and the reply is [current_value, 0]. Callers detect
non-application by checking the applied-increment field; no
error-handling branch is required.
* SATURATE: the result is saturated to UBOUND / LBOUND, or to the type
limits (LLONG_MAX/MIN for BYINT, ±LDBL_MAX for BYFLOAT) when no explicit
bound is given.

New syntax:

    INCREX <key> [BYFLOAT increment | BYINT increment]
                 [LBOUND lowerbound] [UBOUND upperbound] [SATURATE]
[EX seconds | PX milliseconds | EXAT seconds-timestamp | PXAT
milliseconds-timestamp | PERSIST] [ENX]

---------

Co-authored-by: Ozan Tezcan <ozantezcan@gmail.com>
2026-05-20 18:07:14 +08:00
31896140d1 Fix diskless replicas drop during rdb pipe test (#15131)
This PR is based on: valkey-io/valkey#3511
Close https://github.com/redis/redis/issues/14983

## Summary

During diskless replication, if **any single replica** cannot accept a
write (TCP send buffer full / `EAGAIN`), the master stops reading the
RDB pipe entirely, stalling data delivery to **all** replicas —
including fast ones that are ready to receive data.

The failure reason is similar to
https://github.com/redis/redis/pull/14946, the socket buffer is more
easy to fill.

## Root Cause

In `rdbPipeReadHandler`, the master reads from the child's RDB pipe and
writes to all replica sockets in a loop. When `connWrite` to any replica
returns a partial write (socket send buffer full), the handler:

1. Installs a per-replica `rdbPipeWriteHandler` and increments
`rdb_pipe_numconns_writing`
2. **Removes the pipe read event** via `aeDeleteFileEvent(server.el,
server.rdb_pipe_read, AE_READABLE)`, stopping all pipe reads

The pipe read event is only re-enabled when **all** pending write
handlers complete (`rdb_pipe_numconns_writing == 0`), meaning the
**slowest replica dictates the throughput for all replicas**.

## Observed Behavior

With one slow replica (consuming at ~290 KB/s due to `key-load-delay`):

- Master bursts ~1.3 MB of RDB data until the slow replica's socket send
buffer fills
- `rdbPipeReadHandler` disables the pipe read event
- **All replicas starve for 4–5 seconds** while the slow replica drains
its buffer
- Cycle repeats: burst → stall → burst → stall

Ultimately, it leads to a very slow synchronization process of the
entire master and replica.

### Changes

1. Skip the entire `diskless replicas drop during rdb pipe` test under
Valgrind to avoid timing flakiness on slow env.

2. Move `start_server` inside the `foreach all_drop` loop so each
subcase gets a fresh master instead of sharing state across subcases.

3. For `no / slow / fast / all` subcases, replica 0 runs with
`key-load-delay 500`, which combined with the blocked-writer TCP
back-pressure can stall the RDB-saving child indefinitely; shrink the
dataset to ~40 MB so the transfer still exercises the blocked-writer
path but completes in reasonable time instead of hanging on the TCP
deadlock.
For the timeout subcase, replica 0 does not run with `key-load-delay
500`, so to avoid the TCP deadlock we still reduce the dataset somewhat,
but keep it larger than the other subcases. Otherwise the kernel TCP
send buffer can absorb the whole RDB, and we'd miss the
repl_last_partial_write != 0 "(full sync)" timeout path and only hit the
"(streaming sync)" path instead.

5. For the `all` subcase, set `rdb-key-save-delay 1000` on the master so
the RDB child keeps generating data while both replicas are killed,
ensuring the last-replica-drop path is exercised rather than racing with
normal completion.

6. Move the slow-replica `pause_process()` so it happens only in the
timeout subcase, not after killing replicas, so Redis observes the
disconnect promptly in non-timeout flows.

7. In the timeout subcase, set `repl-timeout` 2, wait inline for
`*Disconnecting timedout replica (full sync)*`, then restore
`repl-timeout` 60 so the remaining replica can finish the streamed RDB.

---------

Co-authored-by: Sarthak Aggarwal <sarthagg@amazon.com>  
Co-authored-by: debing.sun <debing.sun@redis.com>
2026-05-19 18:27:33 +08:00
Luca PalmieriandGitHub 842a28f4a1 Configure RediSearch to avoid regenerating C headers for Rust modules (#15220)
Fixes the nightly build breakage.
It requires https://github.com/RediSearch/RediSearch/pull/9669 to be
merged first.
2026-05-19 10:04:29 +03:00
0d06e515af Fix compile on some linux distros (#15225)
Fixes: 

1. After #15096, we pass -flto to jemalloc. On Azure Linux, the
resulting jemalloc library cannot be handled at link time and the build
fails. Adding -ffat-lto-objects so the compiler also emits regular
object code that the linker can fall back to when it cannot handle the
LTO-compiled library.
2. Fixed a warning about `path` being NULL in
`moduleLoadInternalModules()`.
3. Fixed compile warnings on older GCC versions introduced by #15162  
    (reported on Ubuntu 20.04)

Co-authored-by: debing.sun <debing.sun@redis.com>
2026-05-18 15:47:08 +03:00
Mincho PaskalevandGitHub b1a53ea21f Add error on enabling memory tracking in non-clustered mode (#15005)
Enabling memory tracking is forbidden during runtime if it is already
disabled. In non-clustered mode though the checks were incorrect so this
PR enforces the correct behavior in non-clustered environment.
2026-05-15 18:35:07 +03:00
stav-nachmiasandGitHub 0c1c747062 RED-196433 remove unused post release workflow (#15206) 2026-05-14 20:59:17 +03:00
6c3a8eccef Set default for INLINE_LSE_ATOMICS to 0 for compatibility across architectures (#15212)
Ensure backward compatibility and consistent behavior across different
architectures by explicitly setting the default value.

Fixes #15175


Co-authored-by: ofiryanai <ofiryanai1@gmail.com>
2026-05-14 17:14:15 +03:00
2e46d2e735 Hold GCRA out of the release (#15191)
After introducing GCRA algorithm into redis
https://github.com/redis/redis/pull/14826 and subsequent introduction of
new RATE_LIMIT object type - https://github.com/redis/redis/pull/14905.
It was internally decided not to introduce GCRA into the new release.

As still no decision is made on whether it will be kept or not in the
future, this PR only makes the code related to GCRA dead - commands are
inaccessible and AOF/RDB load+save is disabled.

---------

Co-authored-by: debing.sun <debing.sun@redis.com>
2026-05-14 16:31:25 +03:00
Lior KoganandGitHub c3db5254b7 Added Array, rename RQE to Search (#15186) 2026-05-14 14:58:48 +03:00
Omer ShadmiandGitHub 2ad98a4e3c Update RediSearch to v8.7.91 (#15199) 2026-05-14 14:23:32 +03:00
Tom GabsowandGitHub 5faaed6e6b updating Data Types to RC1 (#15201)
JSON - 8.7.91:
* Cherry-pick bug fixes to 8.8 (RedisJSON/RedisJSON#1557)
* MOD-6722 Fix mutation ordering for array commands with recursive paths
(RedisJSON/RedisJSON#1543)
* MOD-7266 make sure to end() deserialization, to disallow trailing c…
(RedisJSON/RedisJSON#1554)
* MOD-14664 Json Path evaluation - Allow multi-result nodelists in filt…
(RedisJSON/RedisJSON#1542)

Bloom - 8.7.91
* Lili: RED-180951 RED-181297 RED-184457 RED-184456 RED-184458
RED-184459 fixing bugs and improving the code
(RedisBloom/RedisBloom#1002)
* MOD-14675 - refresh os list to 8.8 (RedisBloom/RedisBloom#976)

Time Series - 8.7.91:
* MOD-15262 - Align TimeSeries with the RedisModule_GetUserUserName API…
(RedisTimeSeries/RedisTimeSeries#1985)
* MOD-14420 fix count reducers return wrong NaN
(RedisTimeSeries/RedisTimeSeries#2013)
(RedisTimeSeries/RedisTimeSeries#2016)
* Lili- RED-180951 RED-180027 fixing bugs and improving the code
(RedisTimeSeries/RedisTimeSeries#2003)
* MOD-14674 - refresh os list to 8.8
(RedisTimeSeries/RedisTimeSeries#1946)
2026-05-14 14:11:59 +03:00
Scott LasleyandYaacovHazan dbe5d1e605 Fix invalid repl-diskless-load value in replication test (#15178)
Close #15177
Follow [Fix use-after-free when fullsync happens while replica is
running a timed out script
(CVE-2026-23631)](https://github.com/redis/redis/commit/0cca172a174642bdae03b871615227896274d9bb)

Remove the `repl-diskless-load yes` test configuration because this
option exists only in the Redis fork and is not available in Redis OSS.

(cherry picked from commit 5033e15143d100bdc004bc69399b9d013d52ff23)
2026-05-14 14:10:41 +03:00
dannysheynandYaacovHazan 54ea50c029 Fix cluster-announce-ip rejecting hostnames (#15188)
Fixes [#15183](https://github.com/redis/redis/issues/15183).

## Motivation
Commit
[cf668f2c2](https://github.com/redis/redis/commit/cf668f2c2c782ea12dc88458bfd329cf6eb5d658)
tightened cluster-announce-ip validation to require a valid IPv4 or IPv6
address, which is a regression for users that legitimately announce a
hostname.

## Changes
* isValidClusterAnnounceIp() now accepts either:
   * A valid IPv4/IPv6 address
   * A valid hostname — same character rules as cluster-announce-hostname,
length-bounded by NET_IP_STR_LEN to match the storage buffer.

(cherry picked from commit 21f2569f9b577e2acb560f83652e2679c5bd6c92)
2026-05-14 14:10:41 +03:00
ofiryanaiandYaacovHazan c8f1ec959a Limit VADD REDUCE dim to not exceed original dim
* Limit VADD REDUCE dim to not exceed original dim

Enforce VADD key [REDUCE dim]  to reject dim that is bigger than the HNSW original dim, as dimension reduction makes no sense for reduce_dim > original_dim.
This also avoids OOM and possible heap overflow on later allocations using reduce_dim.

This should be backported to Redis version 8.0, 8.2 and 8.4.
2026-05-14 14:10:41 +03:00
bc904dab04 Vector Sets: Limit sane max dim
Co-authored-by: GuyAv46 <47632673+GuyAv46@users.noreply.github.com>
2026-05-14 14:10:41 +03:00
Stav-LeviandYaacovHazan 22f1ab6e27 Fix cluster AUX-field newline/control-character injection bricks a node on restart
* fix cluster AUX-field newline/control-character injection bricks a node on restart
2026-05-14 14:10:41 +03:00
debing.sunandYaacovHazan 5c355b68ec Fix use-after-free when evicting blocked client during unblock (CVE-2026-23479)
When re-executing a pending command after unblocking, check the return value
of `processCommandAndResetClient` and exit if needed.
2026-05-14 14:10:41 +03:00
Ozan TezcanandYaacovHazan 837ca7f8f4 Fix use-after-free when fullsync happens while replica is running a timed out script (CVE-2026-23631)
Fullsync triggers emptyData and scriptingReset which free the scripting/function engine. If a timed out script is still running on the replica, this causes a use-after-free. Delay fullsync processing in readSyncBulkPayload until the script finishes.
2026-05-14 14:10:41 +03:00
Stav-LeviandYaacovHazan 8cdf9391da Fix crash in Lua debugger when error object is not a string 2026-05-14 14:10:41 +03:00
Sergei GeorgievandYaacovHazan c4e3405704 Invalid Memory Access in Redis RESTORE Command (CVE-2026-25243) 2026-05-14 14:10:41 +03:00
sggeorgievandYaacovHazan 80621b1d0e Add DENYOOM flag to SUBSCRIBE, PSUBSCRIBE and SSUBSCRIBE commands
Add the DENYOOM flag to SUBSCRIBE, PSUBSCRIBE, and SSUBSCRIBE commands
to bring their memory protection behavior in line with other Redis commands.

Problem:
Currently, subscribe commands lack memory protection when Redis reaches its
memory limit. This becomes problematic in two specific scenarios:
1. When the eviction policy doesn't allow eviction (e.g., noeviction)
2. When there are no evictable keys remaining in the database

In these cases, memory usage from pub/sub subscribers can keep growing
unchecked, potentially causing the Redis server to run out of memory. This
behavior is inconsistent with other Redis commands, which are protected by
the DENYOOM flag.

Solution:
Add the DENYOOM flag to all subscribe commands. When memory limits are
reached, these commands will be rejected, preventing uncontrolled memory
growth and aligning their behavior with other Redis commands.
2026-05-14 14:10:41 +03:00
Shubham S TapleandGitHub fab099cdcf Fix double free when loading streams with duplicate consumer PEL entries (#15095)
Fixes #15082

## Problem
Loading a stream from RDB/RESTORE with a malformed consumer PEL (the
same pending ID listed twice for one consumer) hit an error path that
called streamFreeNACK() on a nack that was still referenced from the
group’s global PEL (cgroup->pel). Teardown then freed that nack again
while destroying the stream, causing a double free and a possible server
crash.

## Fix
On the duplicate consumer PEL branch in src/rdb.c, stop calling
streamFreeNACK(s, nack) when raxTryInsert(consumer->pel, …) fails. Keep
reporting corruption and rely on decrRefCount(o) for cleanup, consistent
with other paths where the nack is owned only by cgroup->pel.
2026-05-14 14:17:47 +08:00
0d9576435f Implement the new Redis Array type (#15162)
# Redis Array

For years, Redis has been missing a real indexed data structure for the
use cases where the index and the spatial relationship of elements are
semantic. Hashes give you random lookups, but you have to store an index
as a key, and have no range visibility. Lists give you appending and
trimming, but what is in the middle remains hard to access. Streams give
you append-only events, which is another (useful, indeed) beast. None of
these is what you want when the *position itself* has business meaning —
slot 37, step 4, row 18552, day from 2934 to 2949, file line 11, 12, 15
and so forth. And, all those types, for different reasons, are all
suboptimal when you want a **ring buffer** able to store the latest N
observed samples of something.

Up to now, users found ways (they always do \o/) using the fact that the
data structures that are obvious in this universe are also extremely
powerful, if well implemented. But this forces compromises. Arrays
handle these index-first requirements natively, and usually with much
better memory and CPU usage than the workarounds. If the use case is the
right one, Arrays often provide much better space, time and usability at
the same time.

## Internal encoding

1. When dense, an Array is essentially a more fancy C array. You don't
pay anything for storing the index.
2. Yet, instead of going really flat, arrays are sliced into
4096-element slices, and each slice, when it contains just a few
elements, uses a special sparse encoding. When a slice is empty it's
just a `NULL` stored in the directory.
3. Small ints, floats, and short strings are pointer-tagged, so they
cost zero additional memory beyond the pointer slot itself.
4. When very sparse, a super-directory of windowed directories is used.
This allows the data type to be safe, instead of exhibiting pathological
space or time behavior. This representation is only triggered when there
are more than 8 million elements or very high indexes set.

## Use cases

Arrays are mostly stateless if not for the fact that each array
remembers the index of the latest added item, allowing `ARINSERT` and
`ARRING` to work properly. Otherwise it is a set/get at this index game,
with solid support for both setting / getting ranges, server-side
scanning, returning only populated elements in a time which is
proportional not to the range size, but to the population size.

A few concrete examples, that may work as mental models for the set of
problems that are similar to them (from the POV of the data modeling).

**Thermometer.** A sensor reporting once per minute, with gaps:

```
ARSET       temp:room12:day7 123 22.3
ARGETRANGE  temp:room12:day7 600 660     # the 10:00–11:00 window, with NULLs
ARSCAN      temp:room12:day7 600 660     # only populated elements
AROP        temp:room12:day7 0 1439 MAX  # peak of the day, server-side
```

Missing minutes cost little to nothing. Numeric aggregation runs inside
Redis. Telemetry, IoT, meter readings, KPI rollups.

**Calendar.** A clinic with 96 fifteen-minute slots per day:

```
ARSET       sched:room12:day 32 booking:991
ARSCAN      sched:room12:day 0 95      # only occupied slots
ARGETRANGE  sched:room12:day 48 63     # the afternoon full view to render
```

The slot number is the business key in this case. Room booking, parking
spaces, warehouse bins, lockers, ...

**Ring buffer.** ARRING replaces the classic LPUSH+LTRIM pattern.
Imagine remote `dmesg`.

```
ARRING       machine:123 200 "[141087.430123]: arm_cpu_init(): cpu 14 online" # Capped to 200 entries
ARLASTITEMS  machine:123 50 REV       # 50 newest first
```

Faster than LPUSH+LTRIM, keep indexed access to past elements. Last-N
alarms, recent fraud scores, access history, remote logs, device events.
Ok here the use cases are mainly the ones of the old pattern: it is just
a better fit and allows to access random items in the middle, aggregate
server-side, and so forth.

**Workflow.** Step number is the index, value is the status. Gaps are
meaningful:

```
ARSET       claim:99172 0 received
ARSET       claim:99172 3 waiting:reviewer42
ARSET       claim:99172 5 approved
ARGETRANGE  claim:99172 0 5     # full workflow view, with NULLs for missing steps
ARSCAN      claim:99172 0 5     # only steps that have a state
ARCOUNT     claim:99172         # number of recorded steps
ARLEN       claim:99172         # highest reached step + 1
```

**Skills knowledge base for agents.** Arrays are good at representing /
grepping into Markdown files:

```
ARSET   skill:metal_gpu 0 "...."
ARSET   skill:metal_gpu 1 "...."
ARSET   skill:metal_gpu 2 "...."
ARGREP  skill:metal_gpu - + RE "M3|M4" WITHVALUES
```

ARGREP has EXACT, MATCH, GLOB, RE, you can have multiple predicates, can
select AND or OR behavior.

**Bulk import results.** Sparse row annotations over millions of rows /
CSV / ...:

```
ARSET  import:job551 18552 ERR:bad_email
ARSCAN import:job551 0 1000000        # Provides only rows that have something
```

## TLDR

If the position is part of the meaning, use an Array. If you want to
aggregate or grep remotely, use an Array. Feedback welcome :)

---------

Co-authored-by: debing.sun <debing.sun@redis.com>
Co-authored-by: Shubham S Taple <155555100+ShubhamTaple@users.noreply.github.com>
Co-authored-by: Yuan Wang <yuan.wang@redis.com>
Co-authored-by: Marc Gravell <marc.gravell@gmail.com>
2026-05-14 00:56:44 +08:00