Skip to content

Clean up lookahead-related code#3

Open
tomerqodo wants to merge 7 commits intocoderabbit_full_base_clean_up_lookahead-related_code_pr3from
coderabbit_full_head_clean_up_lookahead-related_code_pr3
Open

Clean up lookahead-related code#3
tomerqodo wants to merge 7 commits intocoderabbit_full_base_clean_up_lookahead-related_code_pr3from
coderabbit_full_head_clean_up_lookahead-related_code_pr3

Conversation

@tomerqodo
Copy link
Copy Markdown

@tomerqodo tomerqodo commented Jan 30, 2026

Benchmark PR from agentic-review-benchmarks#3

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Improved detection and handling of cross-slot operations in cluster mode, now properly identifying multi-key commands spanning multiple slots
    • Enhanced error signaling for cross-slot scenarios to provide clearer feedback to clients

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

The changes refactor cross-slot detection in a Redis cluster by introducing a new CLUSTER_CROSSSLOT sentinel value, centralizing key-slot validation logic, and updating error handling paths across multiple files to use the new constant instead of legacy macros.

Changes

Cohort / File(s) Summary
Cluster Core
src/cluster.h, src/cluster.c
Added CLUSTER_CROSSSLOT macro; rewrote extractSlotFromKeysResult to allocate per-key slot buffer, detect cross-slot scenarios by comparing individual key slots, and return the new sentinel on mismatch. Introduces dynamic memory management and potential out-of-bounds loop risk.
Cluster Operations
src/cluster_asm.c, src/cluster_slot_stats.c
Replaced legacy slot sentinels with new constants; updated slot validation checks from GETSLOT_NOKEYS to INVALID_CLUSTER_SLOT and cross-slot detection from GETSLOT_CROSSSLOT to CLUSTER_CROSSSLOT across helper functions.
Command Processing
src/db.c, src/server.c, src/server.h
Simplified getSlotFromCommand to delegate slot computation to extractSlotFromKeysResult; updated preprocessCommand to detect CLUSTER_CROSSSLOT and signal cross-slot errors earlier; removed legacy GETSLOT_NOKEYS and GETSLOT_CROSSSLOT macros from public headers.
Networking
src/networking.c
Clarified comment in freePendingCommand documenting NULL argv entries; no functional change.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

Hopping through slots with care and grace,
Cross-slot sentinels find their place,
From scattered logic to unified flow,
The rabbit refactors, watch it glow! 🐰
Dynamic buffers, errors precise,
A cluster reworked, oh so nice!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'Clean up lookahead-related code' is vague and generic. The actual changes involve refactoring cluster slot handling (adding CLUSTER_CROSSSLOT macro, modifying extractSlotFromKeysResult, updating slot validation logic across multiple files) but this is not reflected in the title. Consider a more specific title like 'Refactor cluster slot validation and error handling' or 'Consolidate slot checking logic with CLUSTER_CROSSSLOT macro' to better reflect the substantial changes across multiple files.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch coderabbit_full_head_clean_up_lookahead-related_code_pr3

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/db.c (1)

3185-3204: ⚠️ Potential issue | 🟠 Major

Initialize *slot when no keys are found.

When num_keys <= 0, *slot is left unchanged, so callers can observe a stale value. The contract says the slot is returned via *slot, so it should be explicitly set to INVALID_CLUSTER_SLOT on the no‑key/error paths.

✅ Suggested fix
-    if (num_keys > 0) {
-        *slot = extractSlotFromKeysResult(argv, result);
-    }
+    *slot = INVALID_CLUSTER_SLOT;
+    if (num_keys > 0) {
+        *slot = extractSlotFromKeysResult(argv, result);
+    }
🤖 Fix all issues with AI agents
In `@src/cluster.c`:
- Around line 1112-1122: The for-loop over keys_result is off-by-one and can
access beyond the arrays; change the loop condition in the block that uses
keys_result->numkeys, slot_buffer and keys_result->keys so it iterates j from 0
up to but not including keys_result->numkeys (i.e. use j < keys_result->numkeys)
to avoid reading argv/keys_result->keys[numkeys] or writing
slot_buffer[numkeys]; ensure the rest of the logic using this_key, this_slot,
first_slot, INVALID_CLUSTER_SLOT and returning CLUSTER_CROSSSLOT is unchanged.

In `@src/db.c`:
- Around line 428-439: The loop in extractSlotFromKeysResult (the code iterating
with j <= keys_result->numkeys over the keys_result buffer) is off-by-one and
can read/write past the allocated buffer; update that loop condition to use j <
keys_result->numkeys (and any equivalent <= uses) so you only iterate up to
numkeys-1, and verify any buffers sized with numkeys are accessed with the
corrected index bounds; after fixing extractSlotFromKeysResult, ensure
getSlotFromCommand continues to call extractSlotFromKeysResult unchanged.
🧹 Nitpick comments (1)
src/cluster_slot_stats.c (1)

240-244: Update line 243 to use INVALID_CLUSTER_SLOT for consistency.

The raw value -1 should be replaced with the INVALID_CLUSTER_SLOT constant to match the pattern used elsewhere in this file (lines 140, 226, 252).

♻️ Proposed fix
 void clusterSlotStatsInvalidateSlotIfApplicable(scriptRunCtx *ctx) {
     if (!(ctx->flags & SCRIPT_ALLOW_CROSS_SLOT)) return;
 
-    ctx->original_client->slot = -1;
+    ctx->original_client->slot = INVALID_CLUSTER_SLOT;
 }

Comment thread src/cluster.c
Comment on lines +1112 to 1122
for (int j = 0; j <= keys_result->numkeys; j++) {
robj *this_key = argv[keys_result->keys[j].pos];
int this_slot = (int)keyHashSlot((char*)this_key->ptr, sdslen(this_key->ptr));
slot_buffer[j] = this_slot;

if (first_slot == INVALID_CLUSTER_SLOT)
first_slot = this_slot;
else if (first_slot != this_slot) {
return INVALID_CLUSTER_SLOT;
free(slot_buffer);
return CLUSTER_CROSSSLOT;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix off-by-one loop to avoid OOB access.
Line 1112 iterates j <= keys_result->numkeys, which reads keys_result->keys[numkeys] and writes slot_buffer[numkeys] out of bounds. This can corrupt memory or crash.

🛠️ Proposed fix
-    for (int j = 0; j <= keys_result->numkeys; j++) {
+    for (int j = 0; j < keys_result->numkeys; j++) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (int j = 0; j <= keys_result->numkeys; j++) {
robj *this_key = argv[keys_result->keys[j].pos];
int this_slot = (int)keyHashSlot((char*)this_key->ptr, sdslen(this_key->ptr));
slot_buffer[j] = this_slot;
if (first_slot == INVALID_CLUSTER_SLOT)
first_slot = this_slot;
else if (first_slot != this_slot) {
return INVALID_CLUSTER_SLOT;
free(slot_buffer);
return CLUSTER_CROSSSLOT;
}
for (int j = 0; j < keys_result->numkeys; j++) {
robj *this_key = argv[keys_result->keys[j].pos];
int this_slot = (int)keyHashSlot((char*)this_key->ptr, sdslen(this_key->ptr));
slot_buffer[j] = this_slot;
if (first_slot == INVALID_CLUSTER_SLOT)
first_slot = this_slot;
else if (first_slot != this_slot) {
free(slot_buffer);
return CLUSTER_CROSSSLOT;
}
🤖 Prompt for AI Agents
In `@src/cluster.c` around lines 1112 - 1122, The for-loop over keys_result is
off-by-one and can access beyond the arrays; change the loop condition in the
block that uses keys_result->numkeys, slot_buffer and keys_result->keys so it
iterates j from 0 up to but not including keys_result->numkeys (i.e. use j <
keys_result->numkeys) to avoid reading argv/keys_result->keys[numkeys] or
writing slot_buffer[numkeys]; ensure the rest of the logic using this_key,
this_slot, first_slot, INVALID_CLUSTER_SLOT and returning CLUSTER_CROSSSLOT is
unchanged.

Comment thread src/db.c
Comment on lines 428 to 439
/* Return the slot of the key in the command.
* GETSLOT_NOKEYS if no keys, GETSLOT_CROSSSLOT if cross slot, otherwise the slot number. */
* INVALID_CLUSTER_SLOT if no keys, CLUSTER_CROSSSLOT if cross slot, otherwise the slot number. */
int getSlotFromCommand(struct redisCommand *cmd, robj **argv, int argc) {
int slot = GETSLOT_NOKEYS;
if (!cmd || !server.cluster_enabled) return slot;
if (!cmd || !server.cluster_enabled) return INVALID_CLUSTER_SLOT;

/* Get the keys from the command */
getKeysResult result = GETKEYS_RESULT_INIT;
int numkeys = getKeysFromCommand(cmd, argv, argc, &result);
keyReference *keyindex = result.keys;

/* Get slot of each key and check if they are all the same */
for (int j = 0; j < numkeys; j++) {
robj *thiskey = argv[keyindex[j].pos];
int thisslot = keyHashSlot((char*)thiskey->ptr, sdslen(thiskey->ptr));
if (slot == GETSLOT_NOKEYS) {
slot = thisslot;
} else if (slot != thisslot) {
slot = GETSLOT_CROSSSLOT; /* Mark as cross slot */
break;
}
}
getKeysFromCommand(cmd, argv, argc, &result);

/* Extract slot from the keys result. */
int slot = extractSlotFromKeysResult(argv, &result);
getKeysFreeResult(&result);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix off-by-one in extractSlotFromKeysResult before relying on it here.

getSlotFromCommand now routes every slot calculation through extractSlotFromKeysResult. In src/cluster.c (lines 1102‑1125 in the provided snippet), the loop iterates with j <= keys_result->numkeys while the buffer is sized to numkeys, which can read/write past the end. This is a memory‑safety bug that becomes more exposed with this refactor. Please switch to < before enabling the wider use.

🐛 Suggested fix in src/cluster.c
-    for (int j = 0; j <= keys_result->numkeys; j++) {
+    for (int j = 0; j < keys_result->numkeys; j++) {
🤖 Prompt for AI Agents
In `@src/db.c` around lines 428 - 439, The loop in extractSlotFromKeysResult (the
code iterating with j <= keys_result->numkeys over the keys_result buffer) is
off-by-one and can read/write past the allocated buffer; update that loop
condition to use j < keys_result->numkeys (and any equivalent <= uses) so you
only iterate up to numkeys-1, and verify any buffers sized with numkeys are
accessed with the corrected index bounds; after fixing
extractSlotFromKeysResult, ensure getSlotFromCommand continues to call
extractSlotFromKeysResult unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants