-
Notifications
You must be signed in to change notification settings - Fork 65
Blog Post: Hash field expiration in Valkey #354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
5994598
to
204cb6b
Compare
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Kyle J. Davis <kyledvs@amazon.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very good blog post.
I left a PR on your fork with several, mostly formatting, and nits.
There area a few more expansive ones here.
|
||
The benchmarks demonstrate that field-level expirations can be added to Valkey without compromising performance or stability. | ||
The memory overhead remains modest and predictable, command throughput is unaffected, and the shared active expiration job efficiently reclaims memory even under heavy ingestion workloads. | ||
Together, these results validate that the coarse-bucket design with adaptive encoding delivers the right balance of efficiency, scalability, and correctness, while preserving Valkey’s reputation for high performance and low latency. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a call to action. What should the reader do next if it was interesting - read the code? look at the tests? just try it out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think adding a section of how to use it with examples of command usage is the right solution?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest next steps of trying out the Valkey 9 rc to try it out. Maybe like to documentation for how to use it.
Also include next steps and ask for people to help!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @madolson.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added 2 sections describing how to examples and future plans. can you PTAL?
fixes some formatting issues
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also like the blog. I left some comments about some stuff that was confusing while reading it.
content/blog/2025-09-15-hash-fields-expiration/hfe-benchmark-hash-commands.png
Show resolved
Hide resolved
Co-authored-by: Madelyn Olson <madelyneolson@gmail.com> Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
2. fix section headers Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alt text looks fine.
Nit: add a bit more to your bio and I think I'm pretty good.
1. Using Hash Field Expiration in Valkey 2. Future plans Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very small changes required.
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall good looking blog post.
 | ||
|
||
This solution introduces a semi-sorted data structure which we named 'vset' (stands for "volatile set"). | ||
The vset manages buckets in different time window resolutions and alternating encodings. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current diagram is helpful, but it may unintentionally suggest that the TTL fields inside the hash map(in the image - content/blog/2025-09-15-hash-fields-expiration/hfe-alternative-skiplist.png) are time-ordered (since the green boxes are laid out in sequence). In reality, the hash table is unordered — TTL fields can be scattered arbitrarily. The only place where time ordering exists is in the TTL index (skiplist or coarse buckets).
To avoid confusion, I’d suggest:
Making the hash map visualization unordered (random scatter of TTL vs non-TTL fields).
Emphasizing that ordering happens exclusively in the skiplist/bucket layer.
Optionally, add a side-by-side view showing Skiplist vs Coarse Buckets, so readers understand both designs clearly.
This is cheap, but untouched keys can linger indefinitely and waste memory. | ||
|
||
* Active expiration – A background cron job runs 10 times per second, sampling a small set of keys with expiration. | ||
Expired keys are deleted until a time budget is reached, ensuring memory is reclaimed proactively without introducing latency spikes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are expirations approximate (bucketed) or exact? What’s the worst-case delay in expiring a field after its set expiration time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cannot provide an answer since this can be user driven. for example, running a 15 minutes LUA script will delay the expiration by at least 15 minutes...
To deliver hash field expirations in a way that is simple, performant, and memory-efficient, we made some deliberate trade-offs in the initial implementation. | ||
As a result, there are a few limitations today that we plan to revisit in future releases: | ||
|
||
1. Expired fields are treated as if they don’t exist when accessed through commands like [HGET](/commands/hget/) or [HGETALL](/commands/hgetall/). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During the execution of MULTI/EXEC, Lua, and functions—are expirations observable mid-transaction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. transactions/scripts are no different than regular keys.
If you try to read or write an expired key, Valkey notices it has passed its TTL and deletes it immediately. | ||
This is cheap, but untouched keys can linger indefinitely and waste memory. | ||
|
||
* Active expiration – A background cron job runs 10 times per second, sampling a small set of keys with expiration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if sweeps fall behind the clock(e.g., high CPU)? Is there a catch-up strategy or rate limit applied?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some ways the job is accelerated when it lags behind. again this is not new to the HFE (as it shares the old mechanism) and I am not sure going into this level of details will do good. for example the job has 2 run modes:
Fast and Slow. the fast mode is running as part of the beforeSleep step which happens every time the server complete writing response to clients. The fast path is something that is managed in terms of CPU cycles.
In practice, this can result in [HRANDFIELD](/commands/hrandfield/) returning an empty reply more often than expected, and it reduces the fairness of field sampling. | ||
3. Valkey normally uses a highly compact representation for small hashes, which saves significant memory. | ||
To simplify the expiration design in Valkey 9.0, we chose to disable this compact encoding when a hash contains volatile fields (i.e., fields with TTLs). | ||
The impact is limited: only small hashes with field expirations are affected, but in those cases memory usage will be slightly higher than the fully compacted representation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we provide metrics for observability - active buckets, members per encoding, sweeps/sec, fields expired/sec, avg/percentile sweep latency, skipped work due to budget.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 9.0 the statistics added are minimal. we wanted to gather some requirements around observability, so we basically added what Redis did (which is not much). I can mention this in the blog, but it will make it look like not much (which is true). I prefer focusing on the algorithm, rather than on observability. I also want to make room for the followup blog when we will improve the active expiration using (I hope) Hierarchal timer wheel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two small changes requested.
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@ranshid I think we're pretty good on this post. According the tracker we're supposed to cluster db post up before this one, but I'd like to love this to publish stage tomorrow (Sept 23). Can you change the publish date in |
I have no objections, but didn't look closely enough to press ship it. @stockholmux Ran is out on holidays for a bit, you might just update and push it. |
Sorry. was out on holidays... Changing to |
@@ -0,0 +1,333 @@ | |||
+++ | |||
title= "Introducing Hash Field Expirations" | |||
date= 2025-09-15 00:00:00 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
date= 2025-09-15 00:00:00 | |
date= 2025-09-25 00:00:00 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went ahead with the Numbered DB post - let's schedule yours for Tuesday the 30th. Please change both the publish date here and the directory.
Description
Blog Post: Introducing Hash Field expiration support
Issues Resolved
closes: #326
Pending tasks
Add section for future work
Add section with example usage
By submitting this pull request, I confirm that my contribution is made under the terms of the BSD-3-Clause License.