Fix HINCRBYFLOAT removes field expiration on replica#39
Conversation
Review Summary by QodoFix HINCRBYFLOAT removing field expiration on replica
WalkthroughsDescription• Fix HINCRBYFLOAT command removing field expiration on replica • Modify hashTypeGetValue to return field expiration time • Propagate HINCRBYFLOAT as HSET+HPEXPIREAT when field has expiration • Add shared object for FIELDS keyword and comprehensive test coverage Diagramflowchart LR
A["HINCRBYFLOAT with expiration"] -->|captures expireat| B["hashTypeGetValue"]
B -->|returns expiration time| C["hincrbyfloatCommand"]
C -->|has_expiration=true| D["Propagate HSET + HPEXPIREAT"]
D -->|maintains expiration| E["Replica field expiration preserved"]
File Changes1. src/server.c
|
Code Review by Qodo
1. unused_flag unused variable
|
| unsigned int vlen; | ||
| int has_expiration = 0; | ||
| uint64_t expireat = EB_EXPIRE_TIME_INVALID; | ||
| int unused_flag = 0; |
There was a problem hiding this comment.
1. unused_flag unused variable 📘 Rule violation ⛯ Reliability
The newly added unused_flag local is never referenced, which will trigger -Wunused-variable and fail builds when warnings are treated as errors. This violates the requirement to compile cleanly with -Werror.
Agent Prompt
## Issue description
`unused_flag` is declared but never used, which can fail compilation when building with `-Werror`.
## Issue Context
The repository requires building cleanly with warnings treated as errors.
## Fix Focus Areas
- src/t_hash.c[2242-2245]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| 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); |
There was a problem hiding this comment.
2. argv[5] out of bounds 📘 Rule violation ✓ Correctness
The new propagation code declares robj *argv[5] but writes argv[5] and passes 6 arguments, which is an out-of-bounds write and can also trigger compile-time diagnostics under -Werror. This violates the requirement to compile cleanly with -Werror.
Agent Prompt
## Issue description
The `argv` array is declared with 5 elements but is populated at index 5 and passed as 6 arguments, causing an out-of-bounds write and potential `-Werror` build failures.
## Issue Context
This occurs in the new manual propagation path added for `HINCRBYFLOAT` when the field has an expiration.
## Fix Focus Areas
- src/t_hash.c[2306-2313]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| GetFieldRes hashTypeGetValue(redisDb *db, robj *o, sds field, unsigned char **vstr, | ||
| unsigned int *vlen, long long *vll, int hfeFlags) { | ||
| uint64_t expiredAt; | ||
| unsigned int *vlen, long long *vll, int hfeFlags, uint64_t *expiredAt) | ||
| { |
There was a problem hiding this comment.
3. hashtypegetvalue not static 📘 Rule violation ⛯ Reliability
The helper function hashTypeGetValue is defined without static, exporting a symbol that appears intended for file-local use. This violates the requirement that internal helper functions be declared static to prevent symbol pollution.
Agent Prompt
## Issue description
`hashTypeGetValue` is a helper defined without `static`, unnecessarily exporting it as a global symbol.
## Issue Context
The function signature was modified in this PR; if it remains internal to `src/t_hash.c`, it should be declared `static` per the encapsulation requirement.
## Fix Focus Areas
- src/t_hash.c[713-715]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| 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); |
There was a problem hiding this comment.
4. Leaked expire timestamp object 🐞 Bug ⛯ Reliability
hincrbyfloatCommand creates a timestamp object for the propagated HPEXPIREAT (createStringObjectFromLongLong(expireat)) and never releases it, leaking one robj per call. Since alsoPropagate increments refcounts, the object will survive after propagation unless explicitly decremented by the caller.
Agent Prompt
### Issue description
A temporary `robj` is created for the `HPEXPIREAT` timestamp and passed to `alsoPropagate`, but it is never decremented afterward. Because `alsoPropagate` increments refcounts, this leads to a per-call memory leak.
### Issue Context
Other Redis codepaths that create temporary argv objects for `alsoPropagate` explicitly `decrRefCount` them after queuing propagation.
### Fix Focus Areas
- src/t_hash.c[2309-2313]
- src/server.c[3322-3334]
- src/t_set.c[831-835]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Benchmark PR from agentic-review-benchmarks#8