Atomic operations with Lua in Redis - Time & Space Complexity
When using Lua scripts in Redis, operations run as one single step without interruption.
We want to see how the time needed grows as the data size changes.
Analyze the time complexity of the following Lua script in Redis.
local key = KEYS[1]
local increment = tonumber(ARGV[1])
local current = redis.call('GET', key)
if not current then
redis.call('SET', key, increment)
else
redis.call('INCRBY', key, increment)
end
return redis.call('GET', key)
This script atomically increments a value stored at a key by a given amount.
Look for repeated actions or loops in the script.
- Primary operation: Single calls to Redis commands (GET, SET, INCRBY)
- How many times: Each command runs once per script execution; no loops or recursion.
The script runs a fixed number of commands regardless of the value size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 3 Redis commands |
| 100 | 3 Redis commands |
| 1000 | 3 Redis commands |
Pattern observation: The number of operations stays the same no matter how big the number is.
Time Complexity: O(1)
This means the time to run the script stays constant no matter the input size.
[X] Wrong: "The script takes longer if the number we add is bigger."
[OK] Correct: Redis commands here work in constant time; the size of the number does not affect how many steps the script takes.
Understanding how atomic Lua scripts run helps you write safe and efficient Redis operations in real projects.
"What if the script included a loop to increment multiple keys? How would the time complexity change?"
Practice
EVAL command?Solution
Step 1: Understand atomicity in Redis
Atomicity means commands run fully or not at all, preventing partial updates.Step 2: Role of Lua scripts with EVAL
Lua scripts run all commands inside them as a single atomic operation in Redis.Final Answer:
It ensures multiple commands run atomically without interference. -> Option AQuick Check:
Atomic operations = safe multi-command execution [OK]
- Thinking Lua scripts speed up server startup
- Confusing script storage with atomicity
- Assuming Lua scripts backup data automatically
counter by 1 using EVAL?Solution
Step 1: Understand EVAL syntax
EVAL script numkeys key1 key2 ... args ...; KEYS[] holds keys, ARGV[] holds arguments.Step 2: Identify correct usage for increment
Since 'counter' is a key, it should be passed as KEYS[1] with numkeys=1.Final Answer:
EVAL "return redis.call('INCR', KEYS[1])" 1 counter -> Option DQuick Check:
Use KEYS for keys, ARGV for arguments [OK]
- Passing keys in ARGV instead of KEYS
- Setting numkeys to 0 when keys are used
- Using string literal instead of KEYS[1]
EVAL below, what will be the output if the key visits does not exist initially?EVAL "local v = redis.call('GET', KEYS[1]) if not v then v = 0 end v = v + 1 redis.call('SET', KEYS[1], v) return v" 1 visitsSolution
Step 1: Analyze GET result when key missing
redis.call('GET', KEYS[1]) returns nil if key doesn't exist.Step 2: Check the if condition
if not v then v = 0 end executes since v is nil, setting v to 0.Step 3: Perform arithmetic and SET
v = 0 + 1 = 1; redis.call('SET', KEYS[1], 1); return 1.Final Answer:
1 -> Option BQuick Check:
Nil handled by if not v, outputs 1 [OK]
- Overlooking the 'if not v then v = 0 end' check
- Assuming nil causes arithmetic error despite handling
- Thinking GET nil leads to error without reading full script
EVAL:EVAL "local count = redis.call('GET', KEYS[1]) count = tonumber(count) + 1 redis.call('SET', KEYS[1], count) return count" 1 visitsSolution
Step 1: Understand tonumber behavior
tonumber(nil) returns nil, so nil + 1 causes error.Step 2: Check script for nil handling
Script does not check if count is nil before adding 1, causing runtime error if key missing.Final Answer:
Missing check if count is nil before tonumber conversion. -> Option AQuick Check:
Always check nil before tonumber and math [OK]
- Assuming tonumber(nil) returns 0
- Ignoring nil checks before arithmetic
- Confusing redis.call and redis.pcall usage
page_views only if its current value is less than 100. Which Lua script snippet correctly implements this logic using EVAL?Solution
Step 1: Handle missing key safely
Using 'or "0"' ensures tonumber gets '0' if key missing, avoiding nil errors.Step 2: Check condition and increment
Condition 'v < 100' matches requirement to increment only if less than 100.Step 3: Return updated or current value
Returns v+1 if incremented, else returns current v.Final Answer:
local v = tonumber(redis.call('GET', KEYS[1]) or '0') if v < 100 then redis.call('INCR', KEYS[1]) return v + 1 else return v end -> Option CQuick Check:
Safe nil handling + correct condition = local v = tonumber(redis.call('GET', KEYS[1]) or '0') if v < 100 then redis.call('INCR', KEYS[1]) return v + 1 else return v end [OK]
- Not handling nil key values
- Using <= 100 instead of < 100
- Forgetting to convert GET result to number
Ready to prove it?
Pass the test and this pattern moves unknown
