[NEW] MSET Command with Expiration Support
The problem/use-case that the feature addresses
Currently, there is no single Valkey command that allows setting multiple keys with expiration in one atomic operation. Users who need to set multiple keys with the same TTL must resort to workarounds that have significant performance and reliability drawbacks.
Current Workarounds and Their Limitations: Approach 1: Pipelining (MSET + Multiple EXPIRE), this will still increase the oneway network round-trip overhead.
MULTI
MSET key1 val1 key2 val2 key3 val3
EXPIRE key1 300
EXPIRE key2 300
EXPIRE key3 300
EXEC
Approach 2: Lua Scripting, scripts must access keys in single slot (without cross-slot capability)
local keys = KEYS
local values = ARGV
local ttl = tonumber(ARGV[1])
for i = 1, #keys do
redis.call('SET', keys[i], values[i+1])
redis.call('EXPIRE', keys[i], ttl)
end
return "OK"
Approach 3: Individual SET Commands with Expiration, lack of atomicity
# Sequential SET commands
SET key1 val1 EX 300
SET key2 val2 EX 300
SET key3 val3 EX 300
Description of the feature
Enhance MSET command to supports atomic setting of multiple key-value pairs with optional expiration, providing a more efficient alternative to using MSET followed by multiple EXPIRE commands.
MSET key value [key value ...] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds]
Parameters key value: One or more key-value pairs to set EX seconds: Set expiration time in seconds (relative) PX milliseconds: Set expiration time in milliseconds (relative) EXAT unix-time-seconds: Set expiration time as Unix timestamp in seconds (absolute) PXAT unix-time-milliseconds: Set expiration time as Unix timestamp in milliseconds (absolute)
Note: Only one expiration option can be specified per command.
Advantages of Enhanced MSET Approach
Single Network Round-Trip
- Dramatic Latency Reduction: Only one command to send and acknowledge
- Bandwidth Efficiency: Minimal protocol overhead compared to multiple commands
- Connection Efficiency: Reduced connection pool pressure
Optimized Server-Side Processing
- Atomic Execution Path: Single code path handles both setting and expiration
- Memory Locality: All operations happen in single function call stack
- Cache Efficiency: Better CPU cache utilization for batch operations
Reduced Replication Overhead
- Compact Replication: Single command replication instead of multiple
- Faster Replica Sync: Less data to transmit to replicas
- Lower AOF Size: More compact persistence format
Alternatives you've considered
Backward Compatibility
The enhanced MSET command is fully backward compatible:
- Existing MSET commands without expiration options work unchanged
- No breaking changes to command behavior
- Maintains the same performance characteristics for non-expiration usage
Additional information
If the issue is accepted, we would like deliver this enhancement .
The enhanced MSET command is fully backward compatible:
- Existing MSET commands without expiration options work unchanged
- No breaking changes to command behavior
Not true.
An existing application that uses keys named "EX", "EXAT", "PX", etc. will break. Example:
MSET EX 10 EXAT 20
Currently, it sets the keys "EX" and "EXAT". It will get a different meaning with the proposed syntax.
There's no way to add new arguments to MSET since it's already variadic and keys and values can be anything. We'd need a new command like MSETEX.
The enhanced MSET command is fully backward compatible:
- Existing MSET commands without expiration options work unchanged
- No breaking changes to command behavior
Not true.
An existing application that uses keys named "EX", "EXAT", "PX", etc. will break. Example:
MSET EX 10 EXAT 20Currently, it sets the keys "EX" and "EXAT". It will get a different meaning with the proposed syntax.
There's no way to add new arguments to MSET since it's already variadic and keys and values can be anything. We'd need a new command like
MSETEX.
We already had this discussion before (although I was unable to locate were). maybe we should change this proposal to suggest MSETEX and wait to see the traction from the community?
There are several requests for commands that combine multiple existing commands into one, with the main motivation being convenience and maybe speed.
We already have
SETEX=SETwithEXargument = Set value and expire time.GETEX= Get value and set expire time.GETSET=SETwithGETargument = Set a value and return the old value.GETDEL=GETandDEL= Delete a key and return the value it had.HSETEX= Set one or more hash fields and their expire time.HGETEX= Get one or more hash fields and set their expire time.
We have requests for (command names TBD):
GETPXT=GETandPEXPIRETIME= Get value and absolute expire time, proposed in #1455.MGETPXT=MGETand multiplePEXPIRETIME= Get value and absolute expire time for multiple keys (same PR as GETPXT).MSETEX= Set multiple keys/values and set their expire time (this issue).
I can imagine more combination of setting/getting value + setting/getting expire time or getting the old value or deleting it:
MGETEX= MultipleGETEX= Get the values of multiple keys and set their expire time.MGETDEL= MultipleGETDEL= Get the values of multiple keys and delete them.MGETSET= MultipleGETSET=MGET+MSET= Set multiple keys and return their old values.HGETPXT=HGET(orHMGET) andHPEXPIRETIME= Get multiple hash fields and their absolute expire times.HGETSET=HGETandHSET= Set a hash field and return the old valueHGETDEL=HGETandHDEL= Delete a hash field and return the old value- ...
It's easy to imagine more variants. It's a balance between benefit and maintainability. When we say no, we often refer to MULTI-EXEC and/or Lua scripts. Recently, we've been optimizing pipelines and transactions, so #2092 might help if speed is the main concern.
OTOH, if the uses cases they're solving are justified enough, we don't have a policy against adding commands like these. As long as commands like these are implemented in relatively independent code, their maintenance cost is expected to be relatively small.
@zuiderkwast (slight rabbit hole...) phew - it gives me a headache when I look at a list like this.
I can imagine more combination of setting/getting value + setting/getting expire time or getting the old value or deleting it: ...
With the numerous variants, it really starts looking something like x86 instruction mnemonics, which is a scary direction. When you say 'It's a balance between benefit and maintainability' maybe we should also add 'approachability' to the things we need to balance?
Adding every combination feels like we're going away from simplicity, but yet I understand that having it for some commands, not others is just arbitrary. I wonder if there is a way to have reusable modifiers to prevent an explosion of commands and learning complexity. I have a few ideas on the subject...
Glad to take this elsewhere since it's not exactly on topic for MSET w/ expiration.
we can unify the options instead of commands?.
Make a small, reusable option grammar that works everywhere: EX|PX|EXAT|PXAT | NX|XX
Examples:
MSET k1 v1 k2 v2 EX 60 NX (no new “MSETEX”).
GET k WITHTTL (no “GETPXT/MGETPXT”).
HSET key f1 v1 f2 v2 EX 60 XX (no “HSETEX+XX” variant).
This will lead to fewer commands, but will complicate the parsing.
Redis are adding this with the following syntax:
MSETEX KEYS numkeys key value [key value …] [XX | NX] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]
For now we will not merge it. @ranshid will decide how we are supposed to annotate this.