Skip to content

feat(hash): add per-field expiration support#1

Open
dashjay wants to merge 10 commits intounstablefrom
feat/support-hash-fields-ttl
Open

feat(hash): add per-field expiration support#1
dashjay wants to merge 10 commits intounstablefrom
feat/support-hash-fields-ttl

Conversation

@dashjay
Copy link
Owner

@dashjay dashjay commented Jan 6, 2026

this pr want to close the origin issue: apache#2269

With reading all codes in this pr:

I came up with a way that differ from these three pr, I did learn alot from 3269 which encode the expire time into value.
But the compatibility is limited. The comment I said here: apache#3269 (comment) told that:

if the user's value was happended to be set to [0xFF][0xFE][1-byte flags][8-byte timestamp if flag set]..... before update, will this key be treat as expired at once ?

I have read all these PRs, and find the key problem of this new feature...

some time/space complexity will become O(N) from O(1)
how is the value encoded the expired time
underlying GC expired key will be cost.
...

So the key problem of add support of hash expiration commands for kvrocks is: Let some(every) key in hash carry a TTL while keeping the read/write hot-path still O(1) and keep all function as origin, correct and fast.

Sorry I can't agree that cmd "hlen" or any other redis command is not correct at any time, kvrocks should be 100% compactable with redis. I can just agree that if one command like "hrangebylex" is origin not exists in redis, that we can modify it's API(better not).

Following is the content of the proposal and trade off things:

INTODUCE

redis command need to be implemented

Old commands (need compatibility)
  • hget: return not found when expired
  • hincrby: hincrby from 0 when expired
  • hincrbyfloat: hincrbyfloat from 0 when expired
  • hset: how the expire time work ?
  • hsetexpire: expire the entire hash
  • hsetnx: ""
  • hdel: return 0 if expired
  • hstrlen: check expired
  • hexists: check expired
  • hlen: big problem, size should minus expired key count
  • hmget: check expired
  • hmset: ""
  • hkeys: filtered the expired keys
  • hvals: filtered the expired values
  • hgetall: filterd the expired key-value pair
  • hscan: a lot expired key will exhaust the CPU
  • hrangebylex: ""
  • hrandfield: a lot expired key will exhaust the CPU

Where to store the timestamp ?

implements pros cons
all timestamp metadata stored in metadata_cf including the expire time of every field backward compatibility cost too much when update (write amp)
encoded the expire time in every value less cost compatibility problem
encoded the expire time in the key less cost compatibility problem
store the expire time in another encoded key less cost good compatibility

there must be an count N which make different implements has better performance, a lot trade-off work need to be done.

If I take store the expire time in another encoded key the origin command will be effect as following:

all read operation will execute twice

rocksdb:get -> rocksdb:get * 2 p99 from 25us -> 50us maybe

following command will be affected:

  • hget, hmget, hkeys, hvals, hgetall, hscan, hrangebylex, hrandfield

and how the key encoded is a problem, I came up with to ways:

  • just store them in another cf (this will bring one more cf for only a small function)
  • just use metadata.version + magic_number for storing the ttls... ( I think this will be better)

HLen problem

ok the final problem is this:

the cmd "hlen" which is origin O(1) but now need to be O(N)

I came up with two ways to solve this problem:

  • metadata.size is still the element count, hlen take O(N) to calculate alive key count
  • provide a new command "hlenrelax" provide not redis-compatable hlen, and provide not accurate count which fixed by compaction_filter

@dashjay dashjay force-pushed the feat/support-hash-fields-ttl branch from 1945d2c to f5ff726 Compare January 6, 2026 14:48
@dashjay dashjay changed the title Feat/support hash fields ttl feat(hash): add per-field expiration support Jan 6, 2026
@dashjay dashjay force-pushed the feat/support-hash-fields-ttl branch from 2430a76 to 8e8e4be Compare January 8, 2026 03:41
@dashjay dashjay force-pushed the feat/support-hash-fields-ttl branch from 8e8e4be to 9dff993 Compare January 21, 2026 07:40
@dashjay dashjay changed the title feat(hash): add per-field expiration support WIP feat(hash): add per-field expiration support Jan 21, 2026
@dashjay dashjay changed the title WIP feat(hash): add per-field expiration support WIP: feat(hash): add per-field expiration support Jan 21, 2026
@dashjay dashjay force-pushed the feat/support-hash-fields-ttl branch 5 times, most recently from bad48a7 to 88be89e Compare January 23, 2026 09:00
@dashjay dashjay changed the title WIP: feat(hash): add per-field expiration support feat(hash): add per-field expiration support Jan 26, 2026
@dashjay dashjay force-pushed the feat/support-hash-fields-ttl branch 5 times, most recently from acfc017 to 701c404 Compare January 27, 2026 06:57
kevin and others added 9 commits February 2, 2026 17:05
Signed-off-by: kevin <kevin@kevin-desktop.lightspeed.mssnks.sbcglobal.net>
Signed-off-by: kevin <kevin@kevin-desktop.lightspeed.mssnks.sbcglobal.net>
Signed-off-by: kevin <kevin@kevin-desktop.lightspeed.mssnks.sbcglobal.net>
Signed-off-by: kevin <kevin@kevin-desktop.lightspeed.mssnks.sbcglobal.net>
Signed-off-by: kevin <kevin@kevin-desktop.lightspeed.mssnks.sbcglobal.net>
Signed-off-by: dashjay <dashjwz@gmail.com>
Signed-off-by: dashjay <dashjwz@gmail.com>
Signed-off-by: dashjay <dashjwz@gmail.com>
@dashjay dashjay force-pushed the feat/support-hash-fields-ttl branch from 701c404 to 5180789 Compare February 2, 2026 09:07
Signed-off-by: dashjay <dashjwz@gmail.com>
@dashjay dashjay force-pushed the feat/support-hash-fields-ttl branch from 707a8bc to 692386b Compare February 3, 2026 02:55
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.

1 participant