I want to use a sorted set to store objects using the redis-server timestamp as score.
I know I can use Redis Streams with * id, but Redis Streams have limitations, including I cannot edit the objects, I cannot use rank or lexicographical sorting, I cannot really delete objects in the middle, unions or intersects, etc.
I want to do this atomically, and use the redis-server timestamp so I can use multiple clients to ZADD without worrying about clock-sync.
How to do this?
The solution is to use a Lua script:
local time = redis.call('TIME')
local ts = time[1]..string.format('%06d', time[2])
return redis.call('ZADD', KEYS[1], ts, ARGV[1])
Here we use Redis TIME command. The command returns:
So we can concatenate these two and use a microsecond-timestamp. We need to zero-pad the microseconds part.
Since sorted sets are good with integer values up to 2^53, our timestamp is safe all the way up to the year 2255.
This is Redis-Cluster-safe as we store in one key. To use multiple keys, make sure to land them on the same node using hash tags if you want to compare timestamps.
You can modify the script to use lower than microsecond resolution.
Here the EVAL command, simple pass key and value as arguments, no need to create the sorted set before hand:
EVAL "local time = redis.call('TIME') local ts = time[1]..string.format('%06d', time[2]) return redis.call('ZADD', KEYS[1], ts, ARGV[1])" 1 ssetKey myVal
As always, you may want to load the script and use EVALSHA.
> SCRIPT LOAD "local time = redis.call('TIME') local ts = time[1]..string.format('%06d', time[2]) return redis.call('ZADD', KEYS[1], ts, ARGV[1])"
"81e366e422d0b09c9b395b5dfe03c03c3b7b3bf7"
> EVALSHA 81e366e422d0b09c9b395b5dfe03c03c3b7b3bf7 1 ssetKey myNewVal
(integer) 1
A note on Redis version. If you are using:
TIME (non-deterministic command) and then write with ZADD.redis.replicate_commands() on top of the script. See Scripts as pure functionsIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With