Skip to content

Fix MEMORY USAGE command#36

Open
tomerqodo wants to merge 2 commits intoqodo_claude_vs_qodo_base_fix_memory_usage_command_pr5from
qodo_claude_vs_qodo_head_fix_memory_usage_command_pr5
Open

Fix MEMORY USAGE command#36
tomerqodo wants to merge 2 commits intoqodo_claude_vs_qodo_base_fix_memory_usage_command_pr5from
qodo_claude_vs_qodo_head_fix_memory_usage_command_pr5

Conversation

@tomerqodo
Copy link
Copy Markdown

Benchmark PR from agentic-review-benchmarks#5

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Fix MEMORY USAGE command memory calculation and kvobj handling

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Rename objectComputeSize to kvobjComputeSize for kvobj type handling
• Fix memory calculation to include object header and key upfront
• Change assignment operators from = to += for proper accumulation
• Fix stream memory calculation bug (use numele instead of numele-1)
• Add comprehensive test coverage for MEMORY USAGE command
Diagram
flowchart LR
  A["objectComputeSize function"] -->|Rename & refactor| B["kvobjComputeSize function"]
  B -->|Initialize asize| C["malloc_usable_size of kvobj"]
  C -->|Accumulate with +=| D["Add type-specific sizes"]
  D -->|Fix stream calc| E["Use numele not numele-1"]
  E -->|Return| F["Accurate memory usage"]
  G["Test coverage"] -->|Add string tests| H["MEMORY USAGE validation"]
Loading

Grey Divider

File Changes

1. src/object.c 🐞 Bug fix +26/-22

Refactor memory calculation for kvobj types

• Rename function from objectComputeSize to kvobjComputeSize to reflect kvobj parameter type
• Initialize asize with malloc_usable_size((void *)o) to include object header and embedded key
 upfront
• Change all size calculations from assignment (=) to accumulation (+=) operators
• Fix stream memory calculation to use s->rax->numele instead of s->rax->numele-1
• Fix quicklist sampling calculation to use samples instead of elecount in division
• Update module memory calculation to use += operator

src/object.c


2. tests/unit/type/hash.tcl 📝 Documentation +1/-1

Update function name in comment

• Update comment to reference new function name kvobjComputeSize

tests/unit/type/hash.tcl


3. tests/unit/type/list.tcl 📝 Documentation +1/-1

Update function name in comment

• Update comment to reference new function name kvobjComputeSize

tests/unit/type/list.tcl


View more (1)
4. tests/unit/type/string.tcl 🧪 Tests +27/-0

Add MEMORY USAGE string encoding tests

• Add comprehensive test MEMORY USAGE - STRINGS covering multiple key and value sizes
• Test both OBJ_ENCODING_EMBSTR and OBJ_ENCODING_RAW encodings with various sizes
• Test OBJ_ENCODING_INT encoding with integer values
• Validate memory usage is within expected bounds (header + key + value sizes)

tests/unit/type/string.tcl


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Mar 10, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

1. kvobjComputeSize not static 📘 Rule violation ⛯ Reliability
Description
kvobjComputeSize is a file-local helper but is declared without static, unnecessarily exporting
the symbol. This violates the requirement to declare internal helper functions as static to avoid
symbol pollution.
Code

src/object.c[1210]

+size_t kvobjComputeSize(robj *key, kvobj *o, size_t sample_size, int dbid) {
Evidence
Compliance rule 7 requires internal helper functions used only within a single source file to be
declared static. The new helper kvobjComputeSize is defined without static and is used from
within src/object.c.

AGENTS.md
src/object.c[1210-1217]
src/object.c[1787-1787]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`kvobjComputeSize` is a helper function defined in `src/object.c` but it is not declared `static`, which exports the symbol unnecessarily.

## Issue Context
Per the compliance requirement, helper/internal functions that are only used within a single source file should be `static` to prevent symbol pollution.

## Fix Focus Areas
- src/object.c[1210-1210]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Non-portable usable-size call 🐞 Bug ⛯ Reliability
Description
kvobjComputeSize() uses malloc_usable_size(o) directly, bypassing Redis’ allocator abstraction;
under USE_JEMALLOC/USE_TCMALLOC this can be undefined (wrong symbol / wrong allocator) or fail to
compile on platforms without malloc_usable_size.
Code

src/object.c[R1216-1217]

+    /* All kv-objects has at least kvobj header and embedded key */
+    size_t asize = malloc_usable_size((void *)o);
Evidence
The code now directly calls malloc_usable_size() for kvobj allocations, but this codebase explicitly
abstracts usable-size queries via zmalloc_size()/zmalloc_usable_size() and maps them to
allocator-specific APIs (e.g., je_malloc_usable_size / tc_malloc_size / malloc_size). Calling
malloc_usable_size directly can be incompatible with the allocator used by zmalloc_* in this build.

src/object.c[1216-1218]
src/zmalloc.h[18-43]
src/zmalloc.h[18-26]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`kvobjComputeSize()` calls `malloc_usable_size()` directly, bypassing the project allocator abstraction. This can break builds (symbol not available) or return invalid sizes / crash when memory was allocated via jemalloc/tcmalloc APIs.

### Issue Context
This repo uses `zmalloc_size()` / `zmalloc_usable_size()` to map to `je_malloc_usable_size`, `tc_malloc_size`, `malloc_size`, etc. `kvobjComputeSize()` should use that abstraction.

### Fix Focus Areas
- src/object.c[1216-1218]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Quicklist average miscomputed 🐞 Bug ✓ Correctness
Description
For OBJ_LIST quicklist encoding, kvobjComputeSize() divides sampled bytes by sampled nodes (samples)
but multiplies by total elements (ql->count), overestimating list memory usage by roughly the
average elements-per-node factor.
Code

src/object.c[R1234-1239]

            do {
                elesize += sizeof(quicklistNode)+zmalloc_size(node->entry);
                elecount += node->count;
                samples++;
            } while ((node = node->next) && samples < sample_size);
-            asize += (double)elesize/elecount*ql->count;
+            asize += (double)elesize/samples*ql->count;
Evidence
quicklist->count is defined as the total number of entries (elements) in the list, while samples
counts the number of quicklist nodes inspected and elecount sums elements across those nodes. Using
elesize/samples produces an average per-node size but then multiplying by ql->count (elements)
mismatches units and inflates the estimate.

src/object.c[1231-1240]
src/quicklist.h[99-112]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The quicklist memory estimation computes an average per *node* (`elesize/samples`) but multiplies it by total *elements* (`ql-&gt;count`), causing inflated MEMORY USAGE for lists.

### Issue Context
Within the sampling loop, `samples` increments once per node, while `elecount` accumulates elements across sampled nodes (`node-&gt;count`). `ql-&gt;count` is the total number of elements.

### Fix Focus Areas
- src/object.c[1231-1240]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Stream listpacks double-counted 🐞 Bug ✓ Correctness
Description
For streams with more listpacks than the sampling budget, kvobjComputeSize() adds average listpack
size multiplied by s->rax->numele and then adds the last listpack’s real size again, overcounting
stream memory usage.
Code

src/object.c[R1327-1333]

            asize += lpsize;
        } else {
            if (samples) lpsize /= samples; /* Compute the average. */
-            asize += lpsize * (s->rax->numele-1);
+            asize += lpsize * s->rax->numele;
            /* No need to check if seek succeeded, we enter this branch only
             * if there are a few elements in the radix tree. */
            raxSeek(&ri,"$",NULL,0);
Evidence
The code enters the estimation branch when s->rax->numele > samples. In that branch it multiplies
the average by numele (counting all listpacks) and then separately adds the last listpack size,
counting the last listpack twice. This also contradicts the nearby comment stating it should
estimate the first N-1 listpacks and then add the real size of the last node.

src/object.c[1311-1314]
src/object.c[1326-1337]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The stream listpack estimation counts `numele` listpacks via the average and then adds the last listpack size again, inflating MEMORY USAGE for streams.

### Issue Context
The code explicitly adds the last listpack after doing an average-based multiplication, and the surrounding comment describes estimating N-1 and then adding the last.

### Fix Focus Areas
- src/object.c[1311-1337]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment thread src/object.c
* are checked and averaged to estimate the total size. */
#define OBJ_COMPUTE_SIZE_DEF_SAMPLES 5 /* Default sample size. */
size_t objectComputeSize(robj *key, robj *o, size_t sample_size, int dbid) {
size_t kvobjComputeSize(robj *key, kvobj *o, size_t sample_size, int dbid) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. kvobjcomputesize not static 📘 Rule violation ⛯ Reliability

kvobjComputeSize is a file-local helper but is declared without static, unnecessarily exporting
the symbol. This violates the requirement to declare internal helper functions as static to avoid
symbol pollution.
Agent Prompt
## Issue description
`kvobjComputeSize` is a helper function defined in `src/object.c` but it is not declared `static`, which exports the symbol unnecessarily.

## Issue Context
Per the compliance requirement, helper/internal functions that are only used within a single source file should be `static` to prevent symbol pollution.

## Fix Focus Areas
- src/object.c[1210-1210]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/object.c
Comment on lines +1216 to +1217
/* All kv-objects has at least kvobj header and embedded key */
size_t asize = malloc_usable_size((void *)o);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Non-portable usable-size call 🐞 Bug ⛯ Reliability

kvobjComputeSize() uses malloc_usable_size(o) directly, bypassing Redis’ allocator abstraction;
under USE_JEMALLOC/USE_TCMALLOC this can be undefined (wrong symbol / wrong allocator) or fail to
compile on platforms without malloc_usable_size.
Agent Prompt
### Issue description
`kvobjComputeSize()` calls `malloc_usable_size()` directly, bypassing the project allocator abstraction. This can break builds (symbol not available) or return invalid sizes / crash when memory was allocated via jemalloc/tcmalloc APIs.

### Issue Context
This repo uses `zmalloc_size()` / `zmalloc_usable_size()` to map to `je_malloc_usable_size`, `tc_malloc_size`, `malloc_size`, etc. `kvobjComputeSize()` should use that abstraction.

### Fix Focus Areas
- src/object.c[1216-1218]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/object.c
Comment on lines 1234 to +1239
do {
elesize += sizeof(quicklistNode)+zmalloc_size(node->entry);
elecount += node->count;
samples++;
} while ((node = node->next) && samples < sample_size);
asize += (double)elesize/elecount*ql->count;
asize += (double)elesize/samples*ql->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.

Action required

3. Quicklist average miscomputed 🐞 Bug ✓ Correctness

For OBJ_LIST quicklist encoding, kvobjComputeSize() divides sampled bytes by sampled nodes (samples)
but multiplies by total elements (ql->count), overestimating list memory usage by roughly the
average elements-per-node factor.
Agent Prompt
### Issue description
The quicklist memory estimation computes an average per *node* (`elesize/samples`) but multiplies it by total *elements* (`ql->count`), causing inflated MEMORY USAGE for lists.

### Issue Context
Within the sampling loop, `samples` increments once per node, while `elecount` accumulates elements across sampled nodes (`node->count`). `ql->count` is the total number of elements.

### Fix Focus Areas
- src/object.c[1231-1240]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/object.c
Comment on lines 1327 to 1333
asize += lpsize;
} else {
if (samples) lpsize /= samples; /* Compute the average. */
asize += lpsize * (s->rax->numele-1);
asize += lpsize * s->rax->numele;
/* No need to check if seek succeeded, we enter this branch only
* if there are a few elements in the radix tree. */
raxSeek(&ri,"$",NULL,0);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Stream listpacks double-counted 🐞 Bug ✓ Correctness

For streams with more listpacks than the sampling budget, kvobjComputeSize() adds average listpack
size multiplied by s->rax->numele and then adds the last listpack’s real size again, overcounting
stream memory usage.
Agent Prompt
### Issue description
The stream listpack estimation counts `numele` listpacks via the average and then adds the last listpack size again, inflating MEMORY USAGE for streams.

### Issue Context
The code explicitly adds the last listpack after doing an average-based multiplication, and the surrounding comment describes estimating N-1 and then adding the last.

### Fix Focus Areas
- src/object.c[1311-1337]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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