Skip to content

Fix adjacent slot range behavior in ASM operations#24

Open
tomerqodo wants to merge 8 commits into
coderabbit_full_base_fix_adjacent_slot_range_behavior_in_asm_operations_pr2from
coderabbit_full_head_fix_adjacent_slot_range_behavior_in_asm_operations_pr2
Open

Fix adjacent slot range behavior in ASM operations#24
tomerqodo wants to merge 8 commits into
coderabbit_full_base_fix_adjacent_slot_range_behavior_in_asm_operations_pr2from
coderabbit_full_head_fix_adjacent_slot_range_behavior_in_asm_operations_pr2

Conversation

@tomerqodo

@tomerqodo tomerqodo commented Jan 29, 2026

Copy link
Copy Markdown

Benchmark PR from agentic-review-benchmarks#2

Summary by CodeRabbit

  • New Features

    • Automatic sorting and merging of adjacent slot ranges in cluster import operations.
    • Enhanced migration event logging now includes key count information.
  • Bug Fixes

    • Improved error handling for import task creation with better resource cleanup.
  • Tests

    • Added tests validating import behavior with unsorted and adjacent slot ranges.

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

@coderabbitai

coderabbitai Bot commented Jan 29, 2026

Copy link
Copy Markdown

Walkthrough

Refactors slot range validation into a normalization-and-validate function that sorts and merges adjacent ranges. Updates import task creation to transfer slot range ownership with centralized error cleanup. Adds utility function to count keys within slot ranges and integrates it into logging and active trim operations.

Changes

Cohort / File(s) Summary
Slot Range Validation Refactoring
src/cluster.h, src/cluster.c
Replaces validateSlotRanges with slotRangeArrayNormalizeAndValidate; adds slotRangeArraySortAndMerge utility to normalize slot ranges by sorting and merging adjacent ranges; updates equality checks and parsing flows to use normalized ranges.
Import Task Ownership and Error Handling
src/cluster_asm.c
Changes import task creation to transfer slot range ownership instead of duplicating; refactors error handling with centralized goto patterns and cleanup paths; adds asmCountKeysInSlots utility for efficient key counting within slot ranges.
Enhanced Logging and Key Counting
src/cluster_asm.c
Replaces manual per-slot key counting with asmCountKeysInSlots utility; expands task event logging to include key counts in IMPORT_COMPLETED, MIGRATE_STARTED, and MIGRATE_COMPLETED messages.
Test Coverage
tests/unit/cluster/atomic-slot-migration.tcl
Adds test cases for IMPORT with unsorted and adjacent slot ranges, verifying normalization, merging, and correct migration state on both nodes.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Parser
    participant Normalization
    participant ImportTask
    participant ErrorHandler

    Client->>Parser: parseSlotRangesOrReply(slots)
    Parser->>Normalization: slotRangeArrayNormalizeAndValidate(slots)
    alt Validation Success
        Normalization->>Normalization: Sort ranges by start
        Normalization->>Normalization: Merge adjacent ranges
        Normalization-->>Parser: Return success
        Parser->>ImportTask: Create task with normalized slots
        ImportTask-->>Client: Task created, ownership transferred
    else Validation Failure
        Normalization-->>ErrorHandler: Return error message
        ErrorHandler->>ErrorHandler: Free slot ranges
        ErrorHandler-->>Client: Return error response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Poem

🐰 Slots now sorted, merged with care,
Adjacent ranges disappear,
Ownership flows through the import way,
Keys are counted, bugs don't stay!
Normalization hops with glee,

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'Fix adjacent slot range behavior in ASM operations' directly aligns with the main changes: normalization and merging of adjacent slot ranges, and improved handling in ASM operations across cluster.c, cluster.h, and cluster_asm.c.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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/cluster.c (1)

1876-1889: Bug: num_ranges comparison occurs before sort/merge, causing false negatives.

The equality check if (slots1->num_ranges != slots2->num_ranges) return 0; happens before the sort and merge operations. After merging, ranges with different initial counts could become equal.

For example:

  • slots1 = [{0,100}, {101,200}] (2 ranges) → merges to [{0,200}] (1 range)
  • slots2 = [{0,200}] (1 range)

These represent identical slots but the function returns 0 (not equal) because 2 != 1.

🐛 Proposed fix
 int slotRangeArrayIsEqual(slotRangeArray *slots1, slotRangeArray *slots2) {
-    if (slots1->num_ranges != slots2->num_ranges) return 0;
-
     slotRangeArraySortAndMerge(slots1);
     slotRangeArraySortAndMerge(slots2);

+    if (slots1->num_ranges != slots2->num_ranges) return 0;
+
     for (int i = 0; i < slots1->num_ranges; i++) {
🤖 Fix all issues with AI agents
In `@src/cluster_asm.c`:
- Around line 1011-1023: The local variable total_ranges in asmCountKeysInSlots
is declared but never used; remove the unused declaration to fix the
-Werror=unused-variable CI failure. Edit the function asmCountKeysInSlots and
delete the line declaring int total_ranges = slots->num_ranges; (or
alternatively use total_ranges in the outer loop condition if you prefer),
leaving the existing loops and key_count logic unchanged.

In `@src/cluster.c`:
- Around line 2014-2019: The validation error string returned in err from
slotRangeArrayNormalizeAndValidate is being freed and discarded instead of being
sent back to the client; update parseSlotRangesOrReply so when
slotRangeArrayNormalizeAndValidate(...) != C_OK it calls the client reply API
(e.g., addReplyError(c, err) or addReplyErrorFormat using the sds) before
freeing err and slotRangeArrayFree(slots), then return NULL; this ensures the
caller/client receives the validation message.
🧹 Nitpick comments (1)
src/cluster.c (1)

1879-1880: Consider documenting or avoiding mutation of input arrays.

slotRangeArrayIsEqual now mutates both input arrays via slotRangeArraySortAndMerge. This side effect is unexpected for a comparison function and could cause issues if callers reuse the arrays after comparison.

Consider either:

  1. Adding a doc comment warning about the mutation, or
  2. Working on copies if preserving original arrays is important
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 175df9e and 0443e9f.

📒 Files selected for processing (4)
  • src/cluster.c
  • src/cluster.h
  • src/cluster_asm.c
  • tests/unit/cluster/atomic-slot-migration.tcl
🧰 Additional context used
🧬 Code graph analysis (3)
src/cluster.h (1)
src/cluster.c (2)
  • slotRangeArraySortAndMerge (1860-1873)
  • slotRangeArrayNormalizeAndValidate (1736-1771)
src/cluster_asm.c (4)
src/cluster_legacy.c (1)
  • getMyClusterNode (5832-5834)
src/sds.c (3)
  • sdsnew (203-206)
  • sdscatprintf (635-642)
  • sdsempty (198-200)
src/cluster.c (3)
  • slotRangeArrayFree (1944-1946)
  • slotRangeArrayDup (1781-1785)
  • slotRangeArrayNormalizeAndValidate (1736-1771)
src/kvstore.c (1)
  • kvstoreDictSize (706-712)
src/cluster.c (2)
src/sds.c (3)
  • sdscatprintf (635-642)
  • sdsempty (198-200)
  • sdsfree (214-217)
src/networking.c (1)
  • addReplyErrorArity (704-707)
🪛 GitHub Actions: CI
src/cluster_asm.c

[error] 1016-1016: unused variable 'total_ranges' [-Werror=unused-variable]. Variable is assigned but never used in asmCountKeysInSlots.

🪛 GitHub Actions: External Server Tests
src/cluster_asm.c

[error] 1016-1016: unused variable 'total_ranges' [-Werror=unused-variable]

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build-old-chain-jemalloc
  • GitHub Check: code-coverage
  • GitHub Check: Analyze (cpp)
  • GitHub Check: code-coverage
🔇 Additional comments (10)
src/cluster.h (1)

187-197: LGTM!

The new function declarations are correctly placed alongside other slotRangeArray functions and follow the existing naming conventions. The signatures align with the implementations in cluster.c.

src/cluster.c (4)

1734-1771: LGTM!

The normalization and validation logic is well-structured: sort/merge first, then validate bounds and detect duplicates using the bitmap. Error messages are clear and informative.


1855-1873: LGTM!

The sort-and-merge implementation is efficient and correct. The in-place algorithm with idx tracking is a good approach, and the explicit note about overlapping ranges not being merged is helpful.


1796-1796: LGTM!

Good defensive check for NULL or empty input, returning an empty string gracefully.


1986-1997: LGTM!

The stricter arity validation ensures at least one complete slot range pair is provided and handles edge cases properly.

tests/unit/cluster/atomic-slot-migration.tcl (1)

514-548: LGTM!

Comprehensive test coverage for the new sorting and merging functionality. The three test cases effectively cover:

  1. Simple adjacent merge
  2. Unsorted input with partial adjacency
  3. Multiple single-slot ranges merging into a contiguous range

Good verification that both source and destination nodes report the correctly merged slot ranges.

src/cluster_asm.c (4)

798-847: Ownership transfer + centralized cleanup looks solid.
The slots ownership change and cleanup path read cleanly and align with the updated flow.


1039-1055: Helpful key-count logging.
Including key counts at import/migrate boundaries improves observability.


2869-2898: Import-start normalization/validation flow looks good.
Dup + normalize + validate before task creation is clean and consistent.


3306-3307: Consistent key counting via helper.
Reusing asmCountKeysInSlots keeps trim metrics aligned with logging.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread src/cluster_asm.c
Comment on lines +1011 to +1023
/* Return the number of keys in the specified slot ranges. */
unsigned long long asmCountKeysInSlots(slotRangeArray *slots) {
if (!slots) return 0;

unsigned long long key_count = 0;
int total_ranges = slots->num_ranges;
for (int i = 0; i < slots->num_ranges; i++) {
for (int j = slots->ranges[i].start; j <= slots->ranges[i].end; j++) {
key_count += kvstoreDictSize(server.db[0].keys, j);
}
}
return key_count;
}

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 CI failure: remove unused total_ranges.
Line 1016 triggers -Werror=unused-variable in CI.

🛠️ Proposed fix
-    int total_ranges = slots->num_ranges;
📝 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
/* Return the number of keys in the specified slot ranges. */
unsigned long long asmCountKeysInSlots(slotRangeArray *slots) {
if (!slots) return 0;
unsigned long long key_count = 0;
int total_ranges = slots->num_ranges;
for (int i = 0; i < slots->num_ranges; i++) {
for (int j = slots->ranges[i].start; j <= slots->ranges[i].end; j++) {
key_count += kvstoreDictSize(server.db[0].keys, j);
}
}
return key_count;
}
/* Return the number of keys in the specified slot ranges. */
unsigned long long asmCountKeysInSlots(slotRangeArray *slots) {
if (!slots) return 0;
unsigned long long key_count = 0;
for (int i = 0; i < slots->num_ranges; i++) {
for (int j = slots->ranges[i].start; j <= slots->ranges[i].end; j++) {
key_count += kvstoreDictSize(server.db[0].keys, j);
}
}
return key_count;
}
🧰 Tools
🪛 GitHub Actions: CI

[error] 1016-1016: unused variable 'total_ranges' [-Werror=unused-variable]. Variable is assigned but never used in asmCountKeysInSlots.

🪛 GitHub Actions: External Server Tests

[error] 1016-1016: unused variable 'total_ranges' [-Werror=unused-variable]

🤖 Prompt for AI Agents
In `@src/cluster_asm.c` around lines 1011 - 1023, The local variable total_ranges
in asmCountKeysInSlots is declared but never used; remove the unused declaration
to fix the -Werror=unused-variable CI failure. Edit the function
asmCountKeysInSlots and delete the line declaring int total_ranges =
slots->num_ranges; (or alternatively use total_ranges in the outer loop
condition if you prefer), leaving the existing loops and key_count logic
unchanged.

Comment thread src/cluster.c
Comment on lines 2014 to 2019
sds err = NULL;
if (validateSlotRanges(slots, &err) != C_OK) {
addReplyErrorSds(c, err);
if (slotRangeArrayNormalizeAndValidate(slots, &err) != C_OK) {
sdsfree(err);
slotRangeArrayFree(slots);
return NULL;
}

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 | 🟠 Major

Validation error message is discarded without being sent to the client.

The function name parseSlotRangesOrReply suggests it should reply with errors, but the validation error from slotRangeArrayNormalizeAndValidate is freed without sending it to the client. The caller receives NULL with no explanation.

🐛 Proposed fix
     sds err = NULL;
     if (slotRangeArrayNormalizeAndValidate(slots, &err) != C_OK) {
+        addReplyErrorSds(c, err);
-        sdsfree(err);
         slotRangeArrayFree(slots);
         return NULL;
     }
📝 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
sds err = NULL;
if (validateSlotRanges(slots, &err) != C_OK) {
addReplyErrorSds(c, err);
if (slotRangeArrayNormalizeAndValidate(slots, &err) != C_OK) {
sdsfree(err);
slotRangeArrayFree(slots);
return NULL;
}
sds err = NULL;
if (slotRangeArrayNormalizeAndValidate(slots, &err) != C_OK) {
addReplyErrorSds(c, err);
slotRangeArrayFree(slots);
return NULL;
}
🤖 Prompt for AI Agents
In `@src/cluster.c` around lines 2014 - 2019, The validation error string returned
in err from slotRangeArrayNormalizeAndValidate is being freed and discarded
instead of being sent back to the client; update parseSlotRangesOrReply so when
slotRangeArrayNormalizeAndValidate(...) != C_OK it calls the client reply API
(e.g., addReplyError(c, err) or addReplyErrorFormat using the sds) before
freeing err and slotRangeArrayFree(slots), then return NULL; this ensures the
caller/client receives the validation message.

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