Skip to content

Fix HINCRBYFLOAT removes field expiration on replica#8

Open
tomerqodo wants to merge 5 commits into
coderabbit_full_base_fix_hincrbyfloat_removes_field_expiration_on_replica_pr8from
coderabbit_full_head_fix_hincrbyfloat_removes_field_expiration_on_replica_pr8
Open

Fix HINCRBYFLOAT removes field expiration on replica#8
tomerqodo wants to merge 5 commits into
coderabbit_full_base_fix_hincrbyfloat_removes_field_expiration_on_replica_pr8from
coderabbit_full_head_fix_hincrbyfloat_removes_field_expiration_on_replica_pr8

Conversation

@tomerqodo

@tomerqodo tomerqodo commented Jan 30, 2026

Copy link
Copy Markdown

Benchmark PR from agentic-review-benchmarks#8

Summary by CodeRabbit

Release Notes

  • New Features

    • Improved hash field expiration handling to ensure expiration times are properly preserved when using HINCRBYFLOAT operations, particularly on replicated servers where command propagation now correctly maintains expiration metadata.
  • Tests

    • Added comprehensive test coverage for HINCRBYFLOAT replication behavior to verify that field expiration times remain intact on replica servers during hash field updates.

@coderabbitai

coderabbitai Bot commented Jan 30, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request introduces field expiration tracking for hash operations. A new shared string object (FIELDS) is initialized, the shared objects structure is extended with a fields member, the hash field retrieval function gains an expiration output parameter with updated call sites, and comprehensive tests validate HINCRBYFLOAT replication behavior with field expiration.

Changes

Cohort / File(s) Summary
Shared Objects Setup
src/server.h, src/server.c
Added fields member to struct sharedObjectsStruct and initialized new shared string constant for FIELDS support.
Hash Field Expiration Core
src/t_hash.c
Extended hashTypeGetValue signature with uint64_t *expiredAt output parameter; updated all call sites to propagate expiration data. Modified expiration comparison logic from >= to > for field validity. Introduced expiration-based propagation sequencing in HINCRBYFLOAT and related commands to ensure proper HSET+HPEXPIREAT ordering.
Test Coverage
tests/unit/type/hash-field-expire.tcl
Added end-to-end test validating HINCRBYFLOAT replication behavior when fields have expiration, verifying expiration preservation and command sequence (HSET+HPEXPIREAT) on replica.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A hop through fields with time to spare,
Expiration tracked with utmost care,
Hash by hash, the data flows,
To replicas where knowledge grows,
With HINCRBYFLOAT's gentle touch,
Time respects our data much! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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 title accurately describes the primary fix: addressing a bug where HINCRBYFLOAT removes field expiration on replicas.

✏️ 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_fix_hincrbyfloat_removes_field_expiration_on_replica_pr8

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.

@tomerqodo

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 30, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tomerqodo

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 30, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tomerqodo

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 30, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@OmriGM

OmriGM commented Feb 2, 2026

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Feb 2, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 3

🤖 Fix all issues with AI agents
In `@src/t_hash.c`:
- Around line 2242-2244: The variable `unused_flag` is declared but never
used—remove its declaration to clean up the function; locate the block where
`int has_expiration = 0; uint64_t expireat = EB_EXPIRE_TIME_INVALID; int
unused_flag = 0;` is defined (in the function in src/t_hash.c) and delete the
`int unused_flag = 0;` part, keeping `has_expiration` and `expireat` intact and
ensuring no other code references `unused_flag`.
- Around line 2306-2314: The argv array is undersized and the dynamically
created string is leaked: change the local array used for propagation to have at
least 6 slots (e.g. robj *argv[6]) so writing argv[5] is safe, keep passing 6 to
alsoPropagate, and after calling alsoPropagate decrement the refcount of the
temporary created object from createStringObjectFromLongLong(expireat) (same
pattern used in propagateHashFieldDeletion) to avoid the memory leak; ensure you
reference argv, createStringObjectFromLongLong, and alsoPropagate when making
the edits.
- Around line 742-743: The expiration boundary at the hash field check is
inconsistent: change the condition in the GETF path from "if (*expiredAt >
(uint64_t) commandTimeSnapshot())" to use ">=" so that fields with expiredAt ==
commandTimeSnapshot() are treated as not expired (consistent with
hashTypeIsExpired()); update the condition to "if (*expiredAt >= (uint64_t)
commandTimeSnapshot()) return GETF_OK;" ensuring consistency across checks that
use commandTimeSnapshot(), including interactions with hashTypeIsExpired() and
other expiration checks.

Comment thread src/t_hash.c
Comment on lines +742 to 743
if (*expiredAt > (uint64_t) commandTimeSnapshot())
return GETF_OK;

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

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the exact code at lines 742-743 and surrounding context
sed -n '735,750p' src/t_hash.c

Repository: agentic-review-benchmarks/redis-combined-coderabbit

Length of output: 604


🏁 Script executed:

# Now search for the hashTypeIsExpired function at line 574
sed -n '570,585p' src/t_hash.c

Repository: agentic-review-benchmarks/redis-combined-coderabbit

Length of output: 547


🏁 Script executed:

# Check around line 3074 for the other expiration check
sed -n '3070,3080p' src/t_hash.c

Repository: agentic-review-benchmarks/redis-combined-coderabbit

Length of output: 444


🏁 Script executed:

# Comprehensive search for all expiration-related comparisons with commandTimeSnapshot
rg "commandTimeSnapshot" src/t_hash.c -B2 -A2 --type c

Repository: agentic-review-benchmarks/redis-combined-coderabbit

Length of output: 3147


🏁 Script executed:

# Also search for other expiration comparisons that might not use commandTimeSnapshot
rg -n "expiredAt|expiresAt" src/t_hash.c --type c -C3

Repository: agentic-review-benchmarks/redis-combined-coderabbit

Length of output: 2810


Address inconsistent expiration boundary conditions in hash field expiration checks.

The condition at line 742 uses > commandTimeSnapshot() to determine if a field is valid, meaning fields with expiredAt == now are considered expired. However, this is inconsistent with hashTypeIsExpired() at line 574, which uses < commandTimeSnapshot() (treating equal as NOT expired), while other checks at line 3074 and elsewhere use <= or < operators. This inconsistency can lead to subtle bugs where the same expiration time is handled differently depending on which code path processes it. Standardize the boundary condition across all expiration checks.

🤖 Prompt for AI Agents
In `@src/t_hash.c` around lines 742 - 743, The expiration boundary at the hash
field check is inconsistent: change the condition in the GETF path from "if
(*expiredAt > (uint64_t) commandTimeSnapshot())" to use ">=" so that fields with
expiredAt == commandTimeSnapshot() are treated as not expired (consistent with
hashTypeIsExpired()); update the condition to "if (*expiredAt >= (uint64_t)
commandTimeSnapshot()) return GETF_OK;" ensuring consistency across checks that
use commandTimeSnapshot(), including interactions with hashTypeIsExpired() and
other expiration checks.

Comment thread src/t_hash.c
Comment on lines +2242 to +2244
int has_expiration = 0;
uint64_t expireat = EB_EXPIRE_TIME_INVALID;
int unused_flag = 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.

⚠️ Potential issue | 🟡 Minor

Remove unused variable unused_flag.

The variable unused_flag is declared but never used in this function.

🧹 Proposed fix
     int has_expiration = 0;
     uint64_t expireat = EB_EXPIRE_TIME_INVALID;
-    int unused_flag = 0;
📝 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
int has_expiration = 0;
uint64_t expireat = EB_EXPIRE_TIME_INVALID;
int unused_flag = 0;
int has_expiration = 0;
uint64_t expireat = EB_EXPIRE_TIME_INVALID;
🤖 Prompt for AI Agents
In `@src/t_hash.c` around lines 2242 - 2244, The variable `unused_flag` is
declared but never used—remove its declaration to clean up the function; locate
the block where `int has_expiration = 0; uint64_t expireat =
EB_EXPIRE_TIME_INVALID; int unused_flag = 0;` is defined (in the function in
src/t_hash.c) and delete the `int unused_flag = 0;` part, keeping
`has_expiration` and `expireat` intact and ensuring no other code references
`unused_flag`.

Comment thread src/t_hash.c
Comment on lines +2306 to +2314
robj *argv[5];
argv[0] = shared.hpexpireat;
argv[1] = c->argv[1];
argv[2] = createStringObjectFromLongLong(expireat);
argv[3] = shared.fields;
argv[4] = shared.integers[1];
argv[5] = c->argv[2];
alsoPropagate(c->db->id, argv, 6, PROPAGATE_AOF|PROPAGATE_REPL);
}

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

Critical: Buffer overflow and memory leak in HPEXPIREAT propagation.

Two issues in this code block:

  1. Buffer overflow: argv is declared with 5 elements (robj *argv[5]), but the code writes to argv[5] (index 5 is out of bounds for a 5-element array) and passes argc=6 to alsoPropagate.

  2. Memory leak: The object created at argv[2] via createStringObjectFromLongLong(expireat) is never freed after alsoPropagate. Following the pattern in propagateHashFieldDeletion (lines 2946-2964), dynamically created objects should be decremented after propagation.

🐛 Proposed fix
         /* Propagate HPEXPIREAT */
-        robj *argv[5];
+        robj *argv[6];
         argv[0] = shared.hpexpireat;
         argv[1] = c->argv[1];
         argv[2] = createStringObjectFromLongLong(expireat);
         argv[3] = shared.fields;
         argv[4] = shared.integers[1];
         argv[5] = c->argv[2];
         alsoPropagate(c->db->id, argv, 6, PROPAGATE_AOF|PROPAGATE_REPL);
+        decrRefCount(argv[2]);
     }
📝 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
robj *argv[5];
argv[0] = shared.hpexpireat;
argv[1] = c->argv[1];
argv[2] = createStringObjectFromLongLong(expireat);
argv[3] = shared.fields;
argv[4] = shared.integers[1];
argv[5] = c->argv[2];
alsoPropagate(c->db->id, argv, 6, PROPAGATE_AOF|PROPAGATE_REPL);
}
robj *argv[6];
argv[0] = shared.hpexpireat;
argv[1] = c->argv[1];
argv[2] = createStringObjectFromLongLong(expireat);
argv[3] = shared.fields;
argv[4] = shared.integers[1];
argv[5] = c->argv[2];
alsoPropagate(c->db->id, argv, 6, PROPAGATE_AOF|PROPAGATE_REPL);
decrRefCount(argv[2]);
}
🤖 Prompt for AI Agents
In `@src/t_hash.c` around lines 2306 - 2314, The argv array is undersized and the
dynamically created string is leaked: change the local array used for
propagation to have at least 6 slots (e.g. robj *argv[6]) so writing argv[5] is
safe, keep passing 6 to alsoPropagate, and after calling alsoPropagate decrement
the refcount of the temporary created object from
createStringObjectFromLongLong(expireat) (same pattern used in
propagateHashFieldDeletion) to avoid the memory leak; ensure you reference argv,
createStringObjectFromLongLong, and alsoPropagate when making the edits.

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