Skip to content

Fix HINCRBYFLOAT removes field expiration on replica#22

Open
tomerqodo wants to merge 2 commits intocoderabbit_combined_20260121_augment_sentry_coderabbit_1_base_fix_hincrbyfloat_removes_field_expiration_on_replica_pr106from
coderabbit_combined_20260121_augment_sentry_coderabbit_1_head_fix_hincrbyfloat_removes_field_expiration_on_replica_pr106
Open

Fix HINCRBYFLOAT removes field expiration on replica#22
tomerqodo wants to merge 2 commits intocoderabbit_combined_20260121_augment_sentry_coderabbit_1_base_fix_hincrbyfloat_removes_field_expiration_on_replica_pr106from
coderabbit_combined_20260121_augment_sentry_coderabbit_1_head_fix_hincrbyfloat_removes_field_expiration_on_replica_pr106

Conversation

@tomerqodo
Copy link

@tomerqodo tomerqodo commented Jan 22, 2026

Benchmark PR from qodo-benchmark#106

Summary by CodeRabbit

  • Bug Fixes

    • Fixed HINCRBYFLOAT command to correctly preserve field expiration times (TTL) during replication to replica instances, ensuring consistency across primary and replica deployments.
  • Tests

    • Added comprehensive test coverage to verify field expiration behavior works correctly with HINCRBYFLOAT across primary and replica replication scenarios.

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

Walkthrough

The changes introduce HSETEX as a new shared command object and modify HINCRBYFLOAT's replication behavior to preserve field expiration times using HSETEX with KEEPTTL instead of HSET with KEEP_TTL flag, with corresponding test coverage for replica synchronization.

Changes

Cohort / File(s) Summary
Shared Object Declarations
src/server.h, src/server.c
Added shared.hsetex and shared.fields string objects to sharedObjectsStruct. These are initialized as globally reusable command name constants for HSETEX and FIELDS respectively.
Hash Command Replication
src/t_hash.c
Modified hincrbyfloatCommand to remove HASH_SET_KEEP_TTL flag from in-memory updates and emit HSETEX command during replication instead of HSET, preserving field TTL through the command vector mechanism.
TTL Preservation Tests
tests/unit/type/hash-field-expire.tcl
Added test cases verifying that HINCRBYFLOAT preserves field expiration on replica nodes, checking both TTL retention and replication stream contents for HSETEX with KEEPTTL.

Sequence Diagram

sequenceDiagram
    participant Master
    participant Replica
    participant ReplicationStream

    Master->>Master: HINCRBYFLOAT executed<br/>(field has TTL)
    Master->>Master: Update field in-memory<br/>(no KEEP_TTL flag)
    Master->>ReplicationStream: Emit HSETEX cmd<br/>(preserves TTL via vector)
    ReplicationStream->>Replica: Transmit HSETEX with KEEPTTL
    Replica->>Replica: Apply HSETEX<br/>(field retains TTL)
    Note over Master,Replica: TTL consistency maintained<br/>across replication
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A hash field hops and grows with grace,
HSETEX keeps the TTL in place!
When masters share their incremented way,
Replicas learn the field won't decay,
KEEPTTL whispers down the stream—
TTL preservation: a fluffy dream! 🥕

🚥 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 directly and clearly describes the main bug fix: resolving an issue where HINCRBYFLOAT incorrectly removes field expiration on replica instances, which matches the primary change across the modified files.

✏️ 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`:
- Line 2559: The call to hashTypeSet should preserve existing field TTLs: modify
the invocation of hashTypeSet in the HINCRBYFLOAT (the line calling
hashTypeSet(c->db, o, c->argv[2]->ptr, new, HASH_SET_TAKE_VALUE)) to include the
HASH_SET_KEEP_TTL flag (i.e., pass HASH_SET_TAKE_VALUE | HASH_SET_KEEP_TTL) so
masters do not drop TTLs and remain consistent with replicas; update the single
call site referencing hashTypeSet in the HINCRBYFLOAT handling code accordingly.
- Around line 2571-2572: The call to rewriteClientCommandVector currently uses
argc=6 but passes seven robj* arguments for the HSETEX command, causing the
final newobj (value) to be omitted; update the argc parameter to 7 in the
rewriteClientCommandVector invocation so the args (shared.hsetex, c->argv[1],
shared.keepttl, shared.fields, shared.integers[1], c->argv[2], newobj) are all
included, and keep the argument order as shown to match the expected HSETEX
<key> KEEPTTL FIELDS 1 <field> <value> replication format.
🧹 Nitpick comments (1)
tests/unit/type/hash-field-expire.tcl (1)

1960-2004: Test verifies replica behavior but should also verify master TTL preservation.

The test correctly validates:

  1. Replication stream contains HSETEX with KEEPTTL
  2. TTL is preserved on replica after HINCRBYFLOAT

However, the test doesn't explicitly verify that TTL is preserved on the master after HINCRBYFLOAT. Given the implementation issues in src/t_hash.c (missing HASH_SET_KEEP_TTL flag), the master would actually lose the TTL while the replica preserves it.

Consider adding an assertion to verify master-side TTL preservation before replication sync:

♻️ Suggested improvement
             r -1 hincrbyfloat h1 f1 1.1
             r -1 hincrbyfloat h1 f2 1.1

+            # Verify TTL is preserved on master after HINCRBYFLOAT
+            assert_range [r -1 httl h1 FIELDS 1 f1] 90 100
+
             # Expiration time should not be removed on replica and the value
             # should be equal to the master.
             wait_for_ofs_sync  [srv -1 client]  [srv 0 client]
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 152df8f and 26b32c2.

📒 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 (1)
src/server.c (1)
src/object.c (1)
  • createStringObject (330-335)
⏰ 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: test-ubuntu-latest
  • GitHub Check: build-centos-jemalloc
  • GitHub Check: test-sanitizer-address
  • GitHub Check: build-32bit
  • GitHub Check: build-debian-old
  • GitHub Check: build-old-chain-jemalloc
  • GitHub Check: code-coverage
  • GitHub Check: test-external-nodebug
  • GitHub Check: test-external-cluster
  • GitHub Check: test-external-standalone
  • GitHub Check: Analyze (cpp)
  • GitHub Check: code-coverage
  • GitHub Check: test-sanitizer-address
  • GitHub Check: build-32bit
  • GitHub Check: build-centos-jemalloc
  • GitHub Check: build-old-chain-jemalloc
  • GitHub Check: build-debian-old
  • GitHub Check: test-ubuntu-latest
  • GitHub Check: test-external-cluster
  • GitHub Check: test-external-standalone
  • GitHub Check: test-external-nodebug
🔇 Additional comments (5)
src/server.h (1)

1529-1531: LGTM!

The addition of *hsetex and *fields shared objects to sharedObjectsStruct is correctly placed and aligns with the PR objective. These shared objects will be used to propagate HINCRBYFLOAT via HSETEX with KEEPTTL to preserve field expiration times on replicas.

src/server.c (1)

2130-2153: LGTM — shared command/arg constants look consistent with existing patterns.

src/t_hash.c (2)

2565-2568: Comment is accurate but implementation is inconsistent.

The comment correctly states that KEEPTTL flag preserves field TTL. However, this only applies to the replicated command. The local hashTypeSet call (line 2559) currently does NOT preserve TTL on the master due to the missing HASH_SET_KEEP_TTL flag.


2576-2576: LGTM - Whitespace/formatting change only; function signature remains unchanged.

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

1970-1976: Test expectations are correct and will catch the argc bug if present.

The expected replication stream patterns like {hsetex h1 KEEPTTL FIELDS 1 f1 *} require 7 tokens. If the argc=6 bug causes the value to be omitted, the pattern matching should fail, making this a good regression test.

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

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);
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

Critical: Missing HASH_SET_KEEP_TTL causes TTL removal on master.

The HASH_SET_KEEP_TTL flag has been removed from the hashTypeSet call. Looking at the hashTypeSet implementation (lines 959-964 and 988-991), when this flag is NOT set and the field has a TTL, the TTL is removed.

This creates master/replica inconsistency:

  • Master: TTL is removed (no HASH_SET_KEEP_TTL)
  • Replica: TTL is preserved (receives HSETEX ... KEEPTTL)

Additionally, the existing test at lines 851-857 expects TTL to be preserved on master:

test {HINCRBYFLOAT - preserve expiration time of the field} {
    ...
    assert_range [r HPTTL h1 FIELDS 1 f1] 1 20
}

Compare with HINCRBY at line 2506 which correctly uses HASH_SET_TAKE_VALUE | HASH_SET_KEEP_TTL.

🐛 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` at line 2559, The call to hashTypeSet should preserve existing
field TTLs: modify the invocation of hashTypeSet in the HINCRBYFLOAT (the line
calling hashTypeSet(c->db, o, c->argv[2]->ptr, new, HASH_SET_TAKE_VALUE)) to
include the HASH_SET_KEEP_TTL flag (i.e., pass HASH_SET_TAKE_VALUE |
HASH_SET_KEEP_TTL) so masters do not drop TTLs and remain consistent with
replicas; update the single call site referencing hashTypeSet in the
HINCRBYFLOAT handling code accordingly.

Comment on lines +2571 to +2572
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

Critical: Wrong argc value causes value to be missing from replicated command.

The rewriteClientCommandVector call specifies argc=6, but 7 robj* arguments are passed:

  1. shared.hsetex - "HSETEX"
  2. c->argv[1] - key
  3. shared.keepttl - "KEEPTTL"
  4. shared.fields - "FIELDS"
  5. shared.integers[1] - "1"
  6. c->argv[2] - field
  7. newobj - value ← This will be excluded!

The expected HSETEX command format requires 7 arguments: HSETEX <key> KEEPTTL FIELDS 1 <field> <value>

With argc=6, the replicated command will be missing the value, causing data corruption on replicas.

🐛 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
rewriteClientCommandVector(c, 6, shared.hsetex, c->argv[1], shared.keepttl,
shared.fields, shared.integers[1], c->argv[2], newobj);
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 2571 - 2572, The call to
rewriteClientCommandVector currently uses argc=6 but passes seven robj*
arguments for the HSETEX command, causing the final newobj (value) to be
omitted; update the argc parameter to 7 in the rewriteClientCommandVector
invocation so the args (shared.hsetex, c->argv[1], shared.keepttl,
shared.fields, shared.integers[1], c->argv[2], newobj) are all included, and
keep the argument order as shown to match the expected HSETEX <key> KEEPTTL
FIELDS 1 <field> <value> replication format.

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