Skip to content

Conversation

@ketkarameya
Copy link

@ketkarameya ketkarameya commented Feb 10, 2026

Code Review Bench PR redis#14637 - Fix adjacent slot range behavior in ASM operations

Benchmark PR for Gitar evaluation

Original PR: agentic-review-benchmarks#2

Fix adjacent slot range behavior in ASM operations


Summary by Gitar

  • Slot range normalization:
    • Added slotRangeArraySortAndMerge() to merge adjacent ranges (e.g., 1000-2000 + 2001-3000 → 1000-3000)
  • Memory management fix:
    • Replaced addReplyErrorSds() with sdsfree() in parseSlotRangesOrReply() to prevent leak
  • Assignment bug fix:
    • Changed += to = in asmActiveTrimStart() to prevent key count accumulation
  • Enhanced logging:
    • Added key counts to import/migrate completion messages via asmCountKeysInSlots()

This will update automatically on new commits.


@gitar-bot-staging
Copy link

gitar-bot-staging bot commented Feb 10, 2026

Code Review ⚠️ Changes requested 0 resolved / 3 findings

Good fix for adjacent slot range handling, but slotRangeArrayIsEqual has a logic bug (compares num_ranges before merging) and parseSlotRangesOrReply silently drops validation errors instead of replying to the client.

⚠️ Bug: slotRangeArrayIsEqual checks count before merging

📄 src/cluster.c:1876-1880

The num_ranges comparison at line 1877 happens before slotRangeArraySortAndMerge() is called at lines 1879-1880. This means two slot range arrays that represent the same set of slots but have different pre-merge num_ranges will incorrectly be reported as not equal.

For example:

  • Array A: {7000-7001, 7002-7003} → num_ranges = 2, merges to {7000-7003} (num_ranges = 1)
  • Array B: {7000-7003} → num_ranges = 1

slotRangeArrayIsEqual(A, B) would return 0 at line 1877 before ever merging, even though both arrays represent slots 7000-7003.

The fix is to move the num_ranges comparison after both slotRangeArraySortAndMerge() calls.

Suggested fix
int slotRangeArrayIsEqual(slotRangeArray *slots1, slotRangeArray *slots2) {
    slotRangeArraySortAndMerge(slots1);
    slotRangeArraySortAndMerge(slots2);

    if (slots1->num_ranges != slots2->num_ranges) return 0;

    for (int i = 0; i < slots1->num_ranges; i++) {
⚠️ Bug: Validation error silently dropped, no reply sent to client

📄 src/cluster.c:2014-2018

In parseSlotRangesOrReply, the validation error message from slotRangeArrayNormalizeAndValidate() is silently freed at line 2016 without sending any error reply to the client. The old code was:

addReplyErrorSds(c, err);  // sends error AND takes ownership of err

The new code is:

sdsfree(err);  // silently discards error message

Since the function name convention is *OrReply (meaning it replies on error), and callers like clusterMigrationCommandImport simply return when parseSlotRangesOrReply returns NULL (trusting that an error was already sent), the client will receive no response at all. This will cause the client connection to hang or timeout.

The fix should restore the error reply to the client. Note that addReplyErrorSds takes ownership of the sds string, so sdsfree(err) should be removed when using it.

Suggested fix
    if (slotRangeArrayNormalizeAndValidate(slots, &err) != C_OK) {
        addReplyErrorSds(c, err);
        slotRangeArrayFree(slots);
        return NULL;
    }
💡 Quality: Unused variable total_ranges in asmCountKeysInSlots

📄 src/cluster_asm.c:1016

The variable total_ranges is assigned at line 1016 (int total_ranges = slots->num_ranges;) but never used anywhere in the function. This will generate a compiler warning with -Wunused-variable (which Redis builds with). It should be removed.

Suggested fix
    unsigned long long key_count = 0;
    for (int i = 0; i < slots->num_ranges; i++) {
Rules 🎸 1 action taken

Gitar Rules

🎸 Summary Enhancement: Appended technical summary with 4 bullet points covering slot range normalization, memory management fix, assignment bug fix, and enhanced logging

5 rules not applicable. Show all rules by commenting gitar display:verbose.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Comment on lines 1876 to +1880
int slotRangeArrayIsEqual(slotRangeArray *slots1, slotRangeArray *slots2) {
if (slots1->num_ranges != slots2->num_ranges) return 0;

/* Sort slot ranges first */
qsort(slots1->ranges, slots1->num_ranges, sizeof(slotRange), compareSlotRange);
qsort(slots2->ranges, slots2->num_ranges, sizeof(slotRange), compareSlotRange);
slotRangeArraySortAndMerge(slots1);
slotRangeArraySortAndMerge(slots2);

Choose a reason for hiding this comment

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

⚠️ Bug: slotRangeArrayIsEqual checks count before merging

The num_ranges comparison at line 1877 happens before slotRangeArraySortAndMerge() is called at lines 1879-1880. This means two slot range arrays that represent the same set of slots but have different pre-merge num_ranges will incorrectly be reported as not equal.

For example:

  • Array A: {7000-7001, 7002-7003} → num_ranges = 2, merges to {7000-7003} (num_ranges = 1)
  • Array B: {7000-7003} → num_ranges = 1

slotRangeArrayIsEqual(A, B) would return 0 at line 1877 before ever merging, even though both arrays represent slots 7000-7003.

The fix is to move the num_ranges comparison after both slotRangeArraySortAndMerge() calls.

Was this helpful? React with 👍 / 👎

Suggested change
int slotRangeArrayIsEqual(slotRangeArray *slots1, slotRangeArray *slots2) {
if (slots1->num_ranges != slots2->num_ranges) return 0;
/* Sort slot ranges first */
qsort(slots1->ranges, slots1->num_ranges, sizeof(slotRange), compareSlotRange);
qsort(slots2->ranges, slots2->num_ranges, sizeof(slotRange), compareSlotRange);
slotRangeArraySortAndMerge(slots1);
slotRangeArraySortAndMerge(slots2);
int slotRangeArrayIsEqual(slotRangeArray *slots1, slotRangeArray *slots2) {
slotRangeArraySortAndMerge(slots1);
slotRangeArraySortAndMerge(slots2);
if (slots1->num_ranges != slots2->num_ranges) return 0;
for (int i = 0; i < slots1->num_ranges; i++) {
  • Apply suggested fix

Comment on lines 2014 to 2018
sds err = NULL;
if (validateSlotRanges(slots, &err) != C_OK) {
addReplyErrorSds(c, err);
if (slotRangeArrayNormalizeAndValidate(slots, &err) != C_OK) {
sdsfree(err);
slotRangeArrayFree(slots);
return NULL;

Choose a reason for hiding this comment

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

⚠️ Bug: Validation error silently dropped, no reply sent to client

In parseSlotRangesOrReply, the validation error message from slotRangeArrayNormalizeAndValidate() is silently freed at line 2016 without sending any error reply to the client. The old code was:

addReplyErrorSds(c, err);  // sends error AND takes ownership of err

The new code is:

sdsfree(err);  // silently discards error message

Since the function name convention is *OrReply (meaning it replies on error), and callers like clusterMigrationCommandImport simply return when parseSlotRangesOrReply returns NULL (trusting that an error was already sent), the client will receive no response at all. This will cause the client connection to hang or timeout.

The fix should restore the error reply to the client. Note that addReplyErrorSds takes ownership of the sds string, so sdsfree(err) should be removed when using it.

Was this helpful? React with 👍 / 👎

Suggested change
sds err = NULL;
if (validateSlotRanges(slots, &err) != C_OK) {
addReplyErrorSds(c, err);
if (slotRangeArrayNormalizeAndValidate(slots, &err) != C_OK) {
sdsfree(err);
slotRangeArrayFree(slots);
return NULL;
if (slotRangeArrayNormalizeAndValidate(slots, &err) != C_OK) {
addReplyErrorSds(c, err);
slotRangeArrayFree(slots);
return NULL;
}
  • Apply suggested fix

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.

3 participants