Skip to content

Fix HINCRBYFLOAT removes field expiration on replica#31

Open
tomerqodo wants to merge 2 commits intocoderabbit_full_base_fix_hincrbyfloat_removes_field_expiration_on_replica_pr9from
coderabbit_full_head_fix_hincrbyfloat_removes_field_expiration_on_replica_pr9
Open

Fix HINCRBYFLOAT removes field expiration on replica#31
tomerqodo wants to merge 2 commits intocoderabbit_full_base_fix_hincrbyfloat_removes_field_expiration_on_replica_pr9from
coderabbit_full_head_fix_hincrbyfloat_removes_field_expiration_on_replica_pr9

Conversation

@tomerqodo
Copy link

@tomerqodo tomerqodo commented Jan 29, 2026

Benchmark PR from agentic-review-benchmarks#9

Summary by CodeRabbit

  • Bug Fixes

    • Fixed HINCRBYFLOAT command to properly preserve field expiration times (TTLs) on replica instances during replication.
  • Tests

    • Added validation tests ensuring HINCRBYFLOAT maintains field expiration behavior during master-replica synchronization.

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

Walkthrough

The changes introduce support for HSETEX command in shared objects and modify HINCRBYFLOAT to replicate with explicit TTL preservation using HSETEX with KEEPTTL flag instead of HSET, along with comprehensive test validation for replica TTL semantics.

Changes

Cohort / File(s) Summary
Shared Object Definitions
src/server.h, src/server.c
Added two new public fields to sharedObjectsStruct: robj *hsetex and robj *fields, initialized in createSharedObjects() to support HSETEX and FIELDS command references throughout the codebase.
Hash Command Replication
src/t_hash.c
Modified HINCRBYFLOAT replication behavior to emit HSETEX with KEEPTTL flag instead of HSET, ensuring explicit TTL preservation for fields with expiration. Removed HASH_SET_KEEP_TTL flag from direct hashTypeSet call and refactored to use rewriteClientCommandVector() for replica command construction.
TTL Preservation Tests
tests/unit/type/hash-field-expire.tcl
Added comprehensive test validating that HINCRBYFLOAT preserves field expiration on replicas by verifying HSETEX/KEEPTTL replication, TTL equality between master and replica, and correct replication stream entries for both expiring and non-expiring fields.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Master as Master Server
    participant Replica as Replica Server

    Client->>Master: HINCRBYFLOAT key field increment
    activate Master
    Master->>Master: hashTypeSet (without KEEP_TTL flag)
    Master->>Master: rewriteClientCommandVector<br/>(construct HSETEX with KEEPTTL)
    deactivate Master
    
    Master->>Replica: HSETEX key field value KEEPTTL
    activate Replica
    Replica->>Replica: Process HSETEX<br/>(preserve existing TTL)
    deactivate Replica
    
    Client->>Replica: Verify field TTL
    Replica-->>Client: TTL preserved ✓
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 With whiskers twitched and nose held high,
I craft a fix for fields that die,
HSETEX blooms where TTLs stay,
Replicas keep time's gentle sway,
No more lost expiration's cry! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 clearly and specifically describes the main change: fixing HINCRBYFLOAT to preserve field expiration on replicas.

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

Copy link

@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

🤖 Fix all issues with AI agents
In `@src/t_hash.c`:
- Around line 2565-2572: The rewriteClientCommandVector call for HINCRBYFLOAT
builds 7 arguments but passes argc=6, which drops the value; update the call to
pass the correct argc (7) so the rewritten HSETEX includes the new value object
(newobj). Locate the rewriteClientCommandVector invocation in t_hash.c (the call
using shared.hsetex, shared.keepttl, shared.fields, shared.integers[1],
c->argv[2], newobj) and change its argc parameter to 7 to match the supplied
arguments.
- Around line 2556-2560: The call to hashTypeSet in the HINCRBYFLOAT update
drops the HASH_SET_KEEP_TTL flag causing field TTLs on the primary to be lost;
restore preservation of TTL by passing HASH_SET_KEEP_TTL along with
HASH_SET_TAKE_VALUE to hashTypeSet (i.e., call hashTypeSet(c->db, o,
c->argv[2]->ptr, new, HASH_SET_TAKE_VALUE | HASH_SET_KEEP_TTL)) so the existing
field expiration is kept consistent with replicas and the documented
HINCRBYFLOAT behavior.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5409da0 and 355e36b.

📒 Files selected for processing (4)
  • src/server.c
  • src/server.h
  • src/t_hash.c
  • tests/unit/type/hash-field-expire.tcl
🧰 Additional context used
🧬 Code graph analysis (2)
src/server.c (1)
src/object.c (1)
  • createStringObject (330-335)
src/t_hash.c (2)
src/networking.c (1)
  • rewriteClientCommandVector (4158-4174)
src/object.c (1)
  • createRawStringObject (131-133)
⏰ 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). (21)
  • GitHub Check: build-centos-jemalloc
  • GitHub Check: test-ubuntu-latest
  • GitHub Check: build-debian-old
  • GitHub Check: build-old-chain-jemalloc
  • GitHub Check: test-sanitizer-address
  • GitHub Check: build-32bit
  • GitHub Check: test-external-cluster
  • GitHub Check: Analyze (cpp)
  • GitHub Check: test-external-nodebug
  • GitHub Check: test-external-standalone
  • GitHub Check: code-coverage
  • GitHub Check: build-old-chain-jemalloc
  • GitHub Check: code-coverage
  • GitHub Check: test-external-cluster
  • GitHub Check: test-external-nodebug
  • GitHub Check: test-external-standalone
  • GitHub Check: build-centos-jemalloc
  • GitHub Check: build-debian-old
  • GitHub Check: test-ubuntu-latest
  • GitHub Check: build-32bit
  • GitHub Check: test-sanitizer-address
🔇 Additional comments (5)
src/server.c (2)

2130-2131: Looks good: shared HSETEX command initialized consistently.

Matches the existing shared command name initialization pattern in this block.


2152-2154: Looks good: shared FIELDS token added in the right section.

Consistent with other shared command-argument tokens in this area.

src/server.h (1)

1529-1531: LGTM!

The new shared object declarations for hsetex and fields follow the established pattern in sharedObjectsStruct. These are correctly placed among related hash command objects and keyword objects respectively, and will be used to support TTL-preserving replication for HINCRBYFLOAT via HSETEX with KEEPTTL.

src/t_hash.c (1)

2576-2597: No issues to flag in this helper.

tests/unit/type/hash-field-expire.tcl (1)

1960-2004: Good coverage for replica TTL preservation and replication stream.
This test clearly validates the new HSETEX+KEEPTTL replication path and checks replica state consistency.

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

Comment on lines 2556 to 2560
char buf[MAX_LONG_DOUBLE_CHARS];
int len = ld2string(buf,sizeof(buf),value,LD_STR_HUMAN);
new = sdsnewlen(buf,len);
hashTypeSet(c->db, o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE | HASH_SET_KEEP_TTL);
hashTypeSet(c->db, o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
addReplyBulkCBuffer(c,buf,len);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve TTL on primary when updating HINCRBYFLOAT.

Line 2559 drops HASH_SET_KEEP_TTL, which removes existing field TTLs on the primary and can diverge from replicas now that KEEPTTL is used in replication. This also contradicts the existing “HINCRBYFLOAT - preserve expiration time of the field” behavior.

🔧 Proposed fix
-    hashTypeSet(c->db, o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
+    hashTypeSet(c->db, o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE | HASH_SET_KEEP_TTL);
🤖 Prompt for AI Agents
In `@src/t_hash.c` around lines 2556 - 2560, The call to hashTypeSet in the
HINCRBYFLOAT update drops the HASH_SET_KEEP_TTL flag causing field TTLs on the
primary to be lost; restore preservation of TTL by passing HASH_SET_KEEP_TTL
along with HASH_SET_TAKE_VALUE to hashTypeSet (i.e., call hashTypeSet(c->db, o,
c->argv[2]->ptr, new, HASH_SET_TAKE_VALUE | HASH_SET_KEEP_TTL)) so the existing
field expiration is kept consistent with replicas and the documented
HINCRBYFLOAT behavior.

Comment on lines +2565 to +2572
/* Always replicate HINCRBYFLOAT as an HSETEX command with the final value
* in order to make sure that differences in float precision or formatting
* will not create differences in replicas or after an AOF restart. */
* will not create differences in replicas or after an AOF restart.
* The KEEPTTL flag is used to make sure the field TTL is preserved. */
robj *newobj;
newobj = createRawStringObject(buf,len);
rewriteClientCommandArgument(c,0,shared.hset);
rewriteClientCommandArgument(c,3,newobj);
rewriteClientCommandVector(c, 6, shared.hsetex, c->argv[1], shared.keepttl,
shared.fields, shared.integers[1], c->argv[2], newobj);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix argc mismatch in rewriteClientCommandVector.

Line 2571 passes 7 arguments but sets argc to 6, which drops the value argument and rewrites an invalid HSETEX command.

🔧 Proposed fix
-    rewriteClientCommandVector(c, 6, shared.hsetex, c->argv[1], shared.keepttl,
+    rewriteClientCommandVector(c, 7, shared.hsetex, c->argv[1], shared.keepttl,
                         shared.fields, shared.integers[1], c->argv[2], newobj);
📝 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
/* Always replicate HINCRBYFLOAT as an HSETEX command with the final value
* in order to make sure that differences in float precision or formatting
* will not create differences in replicas or after an AOF restart. */
* will not create differences in replicas or after an AOF restart.
* The KEEPTTL flag is used to make sure the field TTL is preserved. */
robj *newobj;
newobj = createRawStringObject(buf,len);
rewriteClientCommandArgument(c,0,shared.hset);
rewriteClientCommandArgument(c,3,newobj);
rewriteClientCommandVector(c, 6, shared.hsetex, c->argv[1], shared.keepttl,
shared.fields, shared.integers[1], c->argv[2], newobj);
/* Always replicate HINCRBYFLOAT as an HSETEX command with the final value
* in order to make sure that differences in float precision or formatting
* will not create differences in replicas or after an AOF restart.
* The KEEPTTL flag is used to make sure the field TTL is preserved. */
robj *newobj;
newobj = createRawStringObject(buf,len);
rewriteClientCommandVector(c, 7, shared.hsetex, c->argv[1], shared.keepttl,
shared.fields, shared.integers[1], c->argv[2], newobj);
🤖 Prompt for AI Agents
In `@src/t_hash.c` around lines 2565 - 2572, The rewriteClientCommandVector call
for HINCRBYFLOAT builds 7 arguments but passes argc=6, which drops the value;
update the call to pass the correct argc (7) so the rewritten HSETEX includes
the new value object (newobj). Locate the rewriteClientCommandVector invocation
in t_hash.c (the call using shared.hsetex, shared.keepttl, shared.fields,
shared.integers[1], c->argv[2], newobj) and change its argc parameter to 7 to
match the supplied arguments.

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