Fix how hash is handling overriding of expired fields overwrite#3060
Merged
ranshid merged 13 commits intoJan 26, 2026
Merged
Conversation
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
frostzt
reviewed
Jan 13, 2026
frostzt
reviewed
Jan 13, 2026
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## unstable #3060 +/- ##
============================================
+ Coverage 74.25% 74.38% +0.13%
============================================
Files 129 129
Lines 70988 71124 +136
============================================
+ Hits 52712 52907 +195
+ Misses 18276 18217 -59
🚀 New features to boost your workflow:
|
frostzt
reviewed
Jan 13, 2026
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
cjx-zar
reviewed
Jan 14, 2026
Contributor
|
Since we have import-mode, I think this solution is feasible. |
…cation Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Member
Author
…ided Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
7faa9a2 to
6af3175
Compare
Contributor
|
Sorry, I'm busy with other things today. I'll check tomorrow. |
cjx-zar
reviewed
Jan 17, 2026
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
cjx-zar
reviewed
Jan 21, 2026
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
enjoy-binbin
approved these changes
Jan 25, 2026
enjoy-binbin
left a comment
Member
There was a problem hiding this comment.
Top comment LGTM, i scanned the code (but didn't do a very thorough review), and i trust your changes in HFE part.
Co-authored-by: Binbin <binloveplay1314@qq.com> Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Co-authored-by: Binbin <binloveplay1314@qq.com> Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
murphyjacob4
approved these changes
Jan 26, 2026
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
ranshid
added a commit
to ranshid/valkey
that referenced
this pull request
Jan 26, 2026
…ey-io#3060) There are currently several issues with the existing hash field expiration mechanism: 1. `HINCRBY` is propagated to the replica "as-is". It mean it relies on the fact that the state of the hash is the same on the primary and the replica. HFE did change this assumption as the field might be expired only when the replica will handle the propagated `hincrby`. the problem is that the replica does not "expire" fields by it's own. it needs to respect the request from the primary and always try to use the existing field. This can lead to either miss-alignment with the value on the primary and the replica AND even a disconnection since the replica might hold and "expired" field which is not in "integer" format... 2. HINCRBYFLOAT is currently ALWAYS propagating `hset` - this means that the expiration time of an entry will always be removed on the replica side (it needs to propagate HSETEX when expiration time needs to be maintained) 3. Currently all hash write commands which are mutating values might overwrite an expired field. In such cases the existing implementation will "silently" do so. The problem is that the user will not get any key-space-notificaiton explaining the reason for the behavior. For example, when `hincrby` is issued overwriting an expired field which was not yet "cleaned" by active-expiration it will reset the counter to '0' before incrementing it. this means that the user might ask: why is the value '1' and not bigger, "I did not see any notification that the old value expired"... 4. HSETEX with KEEPTTL suffers from a "somewhat" similar problem as if the primary "replaced" the entry which is expired now but might not have been expired when the primary applied it. There are 2 options for a solution: 1. we could propagate `hdel` for every entry we are "overwritting" (batch them if we can) 2. propagate the commands "by effect". For example - have `hincrby` always propagate either HSET or HSETEX. This will not solve the '#(4)' problem above though, for which we might HAVE to propagate `hdel` I tend to go with the second option. The reason is that it is expected to have less impact on replication stream and should include less processing time on the replicas and network traffic. Specifically for HSETEX with KEEPTTL we will have to propagate the `hdel` in case we overwritten an expired field, but that would help limit the impact of this propagation. --------- Signed-off-by: Ran Shidlansik <ranshid@amazon.com> Co-authored-by: Sourav Singh Rawat <aidenfrostbite@gmail.com> Co-authored-by: Binbin <binloveplay1314@qq.com>
cjx-zar
reviewed
Jan 28, 2026
ranshid
added a commit
to ranshid/valkey
that referenced
this pull request
Jan 28, 2026
…ey-io#3060) There are currently several issues with the existing hash field expiration mechanism: 1. `HINCRBY` is propagated to the replica "as-is". It mean it relies on the fact that the state of the hash is the same on the primary and the replica. HFE did change this assumption as the field might be expired only when the replica will handle the propagated `hincrby`. the problem is that the replica does not "expire" fields by it's own. it needs to respect the request from the primary and always try to use the existing field. This can lead to either miss-alignment with the value on the primary and the replica AND even a disconnection since the replica might hold and "expired" field which is not in "integer" format... 2. HINCRBYFLOAT is currently ALWAYS propagating `hset` - this means that the expiration time of an entry will always be removed on the replica side (it needs to propagate HSETEX when expiration time needs to be maintained) 3. Currently all hash write commands which are mutating values might overwrite an expired field. In such cases the existing implementation will "silently" do so. The problem is that the user will not get any key-space-notificaiton explaining the reason for the behavior. For example, when `hincrby` is issued overwriting an expired field which was not yet "cleaned" by active-expiration it will reset the counter to '0' before incrementing it. this means that the user might ask: why is the value '1' and not bigger, "I did not see any notification that the old value expired"... 4. HSETEX with KEEPTTL suffers from a "somewhat" similar problem as if the primary "replaced" the entry which is expired now but might not have been expired when the primary applied it. There are 2 options for a solution: 1. we could propagate `hdel` for every entry we are "overwritting" (batch them if we can) 2. propagate the commands "by effect". For example - have `hincrby` always propagate either HSET or HSETEX. This will not solve the '#(4)' problem above though, for which we might HAVE to propagate `hdel` I tend to go with the second option. The reason is that it is expected to have less impact on replication stream and should include less processing time on the replicas and network traffic. Specifically for HSETEX with KEEPTTL we will have to propagate the `hdel` in case we overwritten an expired field, but that would help limit the impact of this propagation. --------- Signed-off-by: Ran Shidlansik <ranshid@amazon.com> Co-authored-by: Sourav Singh Rawat <aidenfrostbite@gmail.com> Co-authored-by: Binbin <binloveplay1314@qq.com>
ranshid
added a commit
to ranshid/valkey
that referenced
this pull request
Jan 28, 2026
…ey-io#3060) There are currently several issues with the existing hash field expiration mechanism: 1. `HINCRBY` is propagated to the replica "as-is". It mean it relies on the fact that the state of the hash is the same on the primary and the replica. HFE did change this assumption as the field might be expired only when the replica will handle the propagated `hincrby`. the problem is that the replica does not "expire" fields by it's own. it needs to respect the request from the primary and always try to use the existing field. This can lead to either miss-alignment with the value on the primary and the replica AND even a disconnection since the replica might hold and "expired" field which is not in "integer" format... 2. HINCRBYFLOAT is currently ALWAYS propagating `hset` - this means that the expiration time of an entry will always be removed on the replica side (it needs to propagate HSETEX when expiration time needs to be maintained) 3. Currently all hash write commands which are mutating values might overwrite an expired field. In such cases the existing implementation will "silently" do so. The problem is that the user will not get any key-space-notificaiton explaining the reason for the behavior. For example, when `hincrby` is issued overwriting an expired field which was not yet "cleaned" by active-expiration it will reset the counter to '0' before incrementing it. this means that the user might ask: why is the value '1' and not bigger, "I did not see any notification that the old value expired"... 4. HSETEX with KEEPTTL suffers from a "somewhat" similar problem as if the primary "replaced" the entry which is expired now but might not have been expired when the primary applied it. There are 2 options for a solution: 1. we could propagate `hdel` for every entry we are "overwritting" (batch them if we can) 2. propagate the commands "by effect". For example - have `hincrby` always propagate either HSET or HSETEX. This will not solve the '#(4)' problem above though, for which we might HAVE to propagate `hdel` I tend to go with the second option. The reason is that it is expected to have less impact on replication stream and should include less processing time on the replicas and network traffic. Specifically for HSETEX with KEEPTTL we will have to propagate the `hdel` in case we overwritten an expired field, but that would help limit the impact of this propagation. --------- Signed-off-by: Ran Shidlansik <ranshid@amazon.com> Co-authored-by: Sourav Singh Rawat <aidenfrostbite@gmail.com> Co-authored-by: Binbin <binloveplay1314@qq.com> Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
ranshid
added a commit
that referenced
this pull request
Jan 29, 2026
There are currently several issues with the existing hash field expiration mechanism: 1. `HINCRBY` is propagated to the replica "as-is". It mean it relies on the fact that the state of the hash is the same on the primary and the replica. HFE did change this assumption as the field might be expired only when the replica will handle the propagated `hincrby`. the problem is that the replica does not "expire" fields by it's own. it needs to respect the request from the primary and always try to use the existing field. This can lead to either miss-alignment with the value on the primary and the replica AND even a disconnection since the replica might hold and "expired" field which is not in "integer" format... 2. HINCRBYFLOAT is currently ALWAYS propagating `hset` - this means that the expiration time of an entry will always be removed on the replica side (it needs to propagate HSETEX when expiration time needs to be maintained) 3. Currently all hash write commands which are mutating values might overwrite an expired field. In such cases the existing implementation will "silently" do so. The problem is that the user will not get any key-space-notificaiton explaining the reason for the behavior. For example, when `hincrby` is issued overwriting an expired field which was not yet "cleaned" by active-expiration it will reset the counter to '0' before incrementing it. this means that the user might ask: why is the value '1' and not bigger, "I did not see any notification that the old value expired"... 4. HSETEX with KEEPTTL suffers from a "somewhat" similar problem as if the primary "replaced" the entry which is expired now but might not have been expired when the primary applied it. There are 2 options for a solution: 1. we could propagate `hdel` for every entry we are "overwritting" (batch them if we can) 2. propagate the commands "by effect". For example - have `hincrby` always propagate either HSET or HSETEX. This will not solve the '#(4)' problem above though, for which we might HAVE to propagate `hdel` I tend to go with the second option. The reason is that it is expected to have less impact on replication stream and should include less processing time on the replicas and network traffic. Specifically for HSETEX with KEEPTTL we will have to propagate the `hdel` in case we overwritten an expired field, but that would help limit the impact of this propagation. --------- Signed-off-by: Ran Shidlansik <ranshid@amazon.com> Co-authored-by: Sourav Singh Rawat <aidenfrostbite@gmail.com> Co-authored-by: Binbin <binloveplay1314@qq.com> Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
harrylin98
pushed a commit
to harrylin98/valkey_forked
that referenced
this pull request
Feb 19, 2026
…ey-io#3060) There are currently several issues with the existing hash field expiration mechanism: 1. `HINCRBY` is propagated to the replica "as-is". It mean it relies on the fact that the state of the hash is the same on the primary and the replica. HFE did change this assumption as the field might be expired only when the replica will handle the propagated `hincrby`. the problem is that the replica does not "expire" fields by it's own. it needs to respect the request from the primary and always try to use the existing field. This can lead to either miss-alignment with the value on the primary and the replica AND even a disconnection since the replica might hold and "expired" field which is not in "integer" format... 2. HINCRBYFLOAT is currently ALWAYS propagating `hset` - this means that the expiration time of an entry will always be removed on the replica side (it needs to propagate HSETEX when expiration time needs to be maintained) 3. Currently all hash write commands which are mutating values might overwrite an expired field. In such cases the existing implementation will "silently" do so. The problem is that the user will not get any key-space-notificaiton explaining the reason for the behavior. For example, when `hincrby` is issued overwriting an expired field which was not yet "cleaned" by active-expiration it will reset the counter to '0' before incrementing it. this means that the user might ask: why is the value '1' and not bigger, "I did not see any notification that the old value expired"... 4. HSETEX with KEEPTTL suffers from a "somewhat" similar problem as #(1). the replica will receive the propagated command, but will not know if the primary "replaced" the entry which is expired now but might not have been expired when the primary applied it. There are 2 options for a solution: 1. we could propagate `hdel` for every entry we are "overwritting" (batch them if we can) 2. propagate the commands "by effect". For example - have `hincrby` always propagate either HSET or HSETEX. This will not solve the '#(4)' problem above though, for which we might HAVE to propagate `hdel` I tend to go with the second option. The reason is that it is expected to have less impact on replication stream and should include less processing time on the replicas and network traffic. Specifically for HSETEX with KEEPTTL we will have to propagate the `hdel` in case we overwritten an expired field, but that would help limit the impact of this propagation. --------- Signed-off-by: Ran Shidlansik <ranshid@amazon.com> Co-authored-by: Sourav Singh Rawat <aidenfrostbite@gmail.com> Co-authored-by: Binbin <binloveplay1314@qq.com>
hpatro
pushed a commit
to hpatro/valkey
that referenced
this pull request
Mar 5, 2026
…ey-io#3060) There are currently several issues with the existing hash field expiration mechanism: 1. `HINCRBY` is propagated to the replica "as-is". It mean it relies on the fact that the state of the hash is the same on the primary and the replica. HFE did change this assumption as the field might be expired only when the replica will handle the propagated `hincrby`. the problem is that the replica does not "expire" fields by it's own. it needs to respect the request from the primary and always try to use the existing field. This can lead to either miss-alignment with the value on the primary and the replica AND even a disconnection since the replica might hold and "expired" field which is not in "integer" format... 2. HINCRBYFLOAT is currently ALWAYS propagating `hset` - this means that the expiration time of an entry will always be removed on the replica side (it needs to propagate HSETEX when expiration time needs to be maintained) 3. Currently all hash write commands which are mutating values might overwrite an expired field. In such cases the existing implementation will "silently" do so. The problem is that the user will not get any key-space-notificaiton explaining the reason for the behavior. For example, when `hincrby` is issued overwriting an expired field which was not yet "cleaned" by active-expiration it will reset the counter to '0' before incrementing it. this means that the user might ask: why is the value '1' and not bigger, "I did not see any notification that the old value expired"... 4. HSETEX with KEEPTTL suffers from a "somewhat" similar problem as #(1). the replica will receive the propagated command, but will not know if the primary "replaced" the entry which is expired now but might not have been expired when the primary applied it. There are 2 options for a solution: 1. we could propagate `hdel` for every entry we are "overwritting" (batch them if we can) 2. propagate the commands "by effect". For example - have `hincrby` always propagate either HSET or HSETEX. This will not solve the '#(4)' problem above though, for which we might HAVE to propagate `hdel` I tend to go with the second option. The reason is that it is expected to have less impact on replication stream and should include less processing time on the replicas and network traffic. Specifically for HSETEX with KEEPTTL we will have to propagate the `hdel` in case we overwritten an expired field, but that would help limit the impact of this propagation. --------- Signed-off-by: Ran Shidlansik <ranshid@amazon.com> Co-authored-by: Sourav Singh Rawat <aidenfrostbite@gmail.com> Co-authored-by: Binbin <binloveplay1314@qq.com> Signed-off-by: Harkrishn Patro <bunty.hari@gmail.com>
lmagomes
pushed a commit
to lmagomes/home-services
that referenced
this pull request
May 12, 2026
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [docker.io/valkey/valkey](https://github.com/valkey-io/valkey) | image | patch | `9.0.1` → `9.0.4` | --- ### Release Notes <details> <summary>valkey-io/valkey (docker.io/valkey/valkey)</summary> ### [`v9.0.4`](https://github.com/valkey-io/valkey/releases/tag/9.0.4) [Compare Source](valkey-io/valkey@9.0.3...9.0.4) Upgrade urgency SECURITY: This release includes security fixes we recommend you apply as soon as possible. ##### Security fixes - (CVE-2026-23479) Use-After-Free in unblock client flow - (CVE-2026-25243) Invalid Memory Access in RESTORE command - (CVE-2026-23631) Use-after-free when full sync occurs during a yielding Lua/function execution ### [`v9.0.3`](https://github.com/valkey-io/valkey/releases/tag/9.0.3) [Compare Source](valkey-io/valkey@9.0.2...9.0.3) ##### Valkey 9.0.3 Upgrade urgency SECURITY: This release includes security fixes we recommend you apply as soon as possible. ##### Security fixes - (CVE-2025-67733) RESP Protocol Injection via Lua error\_reply - (CVE-2026-21863) Remote DoS with malformed Valkey Cluster bus message - (CVE-2026-27623) Reset request type after handling empty requests ##### Bug fixes - Avoids crash during MODULE UNLOAD when ACL rules reference a module command and subcommand ([#​3160](valkey-io/valkey#3160)) - Fix server assert on ACL LOAD when current user loses permission to channels ([#​3182](valkey-io/valkey#3182)) - Fix bug causing no response flush sometimes when IO threads are busy ([#​3205](valkey-io/valkey#3205)) ### [`v9.0.2`](https://github.com/valkey-io/valkey/releases/tag/9.0.2) [Compare Source](valkey-io/valkey@9.0.1...9.0.2) Upgrade urgency HIGH: There are critical bugs that may affect a subset of users. #### Bug fixes - Avoid memory leak of new argv when HEXPIRE commands target only non-exiting fields ([#​2973](valkey-io/valkey#2973)) - Fix HINCRBY and HINCRBYFLOAT to update volatile key tracking ([#​2974](valkey-io/valkey#2974)) - Avoid empty hash object when HSETEX added no fields ([#​2998](valkey-io/valkey#2998)) - Fix case-sensitive check for the FNX and FXX arguments in HSETEX ([#​3000](valkey-io/valkey#3000)) - Prevent assertion in active expiration job after a hash with volatile fields is overwritten ([#​3003](valkey-io/valkey#3003), [#​3007](valkey-io/valkey#3007)) - Fix HRANDFIELD to return null response when no field could be found ([#​3022](valkey-io/valkey#3022)) - Fix HEXPIRE to not delete items when validation rules fail and expiration is in the past ([#​3023](valkey-io/valkey#3023), [#​3048](valkey-io/valkey#3048)) - Fix how hash is handling overriding of expired fields overwrite ([#​3060](valkey-io/valkey#3060)) - HSETEX - Always issue keyspace notifications after validation ([#​3001](valkey-io/valkey#3001)) - Make zero a valid TTL for hash fields during import mode and data loading ([#​3006](valkey-io/valkey#3006)) - Trigger prepareCommand on argc change in module command filters ([#​2945](valkey-io/valkey#2945)) - Restrict TTL from being negative and avoid crash in import-mode ([#​2944](valkey-io/valkey#2944)) - Fix chained replica crash when doing dual channel replication ([#​2983](valkey-io/valkey#2983)) - Skip slot cache optimization for AOF client to prevent key duplication and data corruption ([#​3004](valkey-io/valkey#3004)) - Fix used\_memory\_dataset underflow due to miscalculated used\_memory\_overhead ([#​3005](valkey-io/valkey#3005)) - Avoid duplicate calculations of network-bytes-out in slot stats with copy-avoidance ([#​3046](valkey-io/valkey#3046)) - Fix XREAD returning error on empty stream with + ID ([#​2742](valkey-io/valkey#2742)) #### Performance/Efficiency Improvements - Track reply bytes in I/O threads if commandlog-reply-larger-than is -1 ([#​3086](valkey-io/valkey#3086), [#​3126](valkey-io/valkey#3126)). This makes it possible to mitigate a performance regression in 9.0.1 caused by the bug fix [#​2652](valkey-io/valkey#2652). **Full Changelog**: <valkey-io/valkey@9.0.1...9.0.2> </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "before 6am" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjE2OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
There are currently several issues with the existing hash field expiration mechanism:
HINCRBYis propagated to the replica "as-is". It mean it relies on the fact that the state of the hash is the same on the primary and the replica. HFE did change this assumption as the field might be expired only when the replica will handle the propagatedhincrby. the problem is that the replica does not "expire" fields by it's own. it needs to respect the request from the primary and always try to use the existing field. This can lead to either miss-alignment with the value on the primary and the replica AND even a disconnection since the replica might hold and "expired" field which is not in "integer" format...hset- this means that the expiration time of an entry will always be removed on the replica side (it needs to propagate HSETEX when expiration time needs to be maintained)hincrbyis issued overwriting an expired field which was not yet "cleaned" by active-expiration it will reset the counter to '0' before incrementing it. this means that the user might ask: why is the value '1' and not bigger, "I did not see any notification that the old value expired"...There are 2 options for a solution:
hdelfor every entry we are "overwritting" (batch them if we can)hincrbyalways propagate either HSET or HSETEX. This will not solve the '#(4)' problem above though, for which we might HAVE to propagatehdelI tend to go with the second option. The reason is that it is expected to have less impact on replication stream and should include less processing time on the replicas and network traffic. Specifically for HSETEX with KEEPTTL we will have to propagate the
hdelin case we overwritten an expired field, but that would help limit the impact of this propagation.