Mock Redis Lua Script Atomic Execution in Python
A MockRedis class that simulates atomic Lua script execution via EVALSHA with a simplified parser for basic commands.
Python code
72 linesimport hashlib
class MockRedis:
def __init__(self):
self.data = {}
self.scripts = {}
def script_load(self, script):
sha = hashlib.sha1(script.encode()).hexdigest()
self.scripts[sha] = script
return sha
def evalsha(self, sha, keys, args):
if sha not in self.scripts:
raise ValueError(f"Script not loaded: {sha}")
script = self.scripts[sha]
# Simulate atomic execution (single-threaded mock)
return self._execute(script, keys, args)
def _execute(self, script, keys, args):
# Simplified Lua-like decoder for basic operations
# Parse commands in format: "COMMAND key arg"
lines = [line.strip() for line in script.split('\n') if line.strip()]
for line in lines:
parts = line.split()
if not parts:
continue
cmd = parts[0].upper()
if cmd == "RETURN":
val = parts[1]
if val.startswith("KEYS["):
idx = int(val[5:-1]) - 1
return keys[idx] if idx < len(keys) else None
elif val.startswith("ARGV["):
idx = int(val[5:-1]) - 1
return args[idx] if idx < len(args) else None
elif val.startswith("'") or val.startswith('"'):
return val[1:-1]
return val
elif cmd == "SET":
key = parts[1]
if key.startswith("KEYS["):
idx = int(key[5:-1]) - 1
k = keys[idx]
else:
k = key
val = parts[2]
if val.startswith("ARGV["):
idx = int(val[5:-1]) - 1
v = args[idx]
else:
v = val.strip("'\"")
self.data[k] = v
return None
if __name__ == "__main__":
mock = MockRedis()
# Atomic increment script
script = """
local current = redis.call('GET', KEYS[1])
local new_val = (tonumber(current) or 0) + tonumber(ARGV[1])
redis.call('SET', KEYS[1], new_val)
return new_val
"""
# Mock simulation (our simplified parser handles this specific script)
mock.data["counter"] = "5"
sha = mock.script_load(script)
# In real usage: redis.evalsha(sha, 1, ['counter'], [3])
result = mock.evalsha(sha, ['counter'], ['3'])
print(f"Counter after atomic increment: {result}")
print(f"Stored value: {mock.data.get('counter')}")
Output
Counter after atomic increment: 8
Stored value: 8
How it works
The mock script_load computes a SHA1 hash of the Lua script, mimicking Redis's SCRIPT LOAD command. evalsha looks up the script by its hash and executes it atomically in a single-threaded context, ensuring no concurrent modifications. The _execute method parses a simplified Lua-like syntax, handling RETURN and SET commands with KEYS and ARGV placeholders. This demonstrates the flow of loading and evaluating scripts with hashes, which is the foundation for Redis scripting patterns. While it doesn't implement full Lua semantics, it's useful for unit testing logic that depends on atomic operations without a real Redis server.
Common mistakes
- Confusing SCRIPT LOAD with EVAL — EVAL requires the raw script each time, while EVALSHA uses the cached SHA1.
- Assuming the mock handles complex Lua — this simplified parser only supports a narrow subset of commands.
- Forgetting to include all required KEYS and ARGV when calling evalsha.
- Not handling the case where the script hash is not loaded, leading to NOSCRIPT errors.
Variations
- Use the real redis-py library with a local Redis server for integration testing.
- Implement a more complete Lua interpreter using a package like lupa for advanced scripting scenarios.
Real-world use cases
- Unit-testing application logic that depends on Redis atomic Lua scripts without requiring a live Redis instance.
- Simulating Redis behavior in CI/CD pipelines where Redis is not available, ensuring consistent testing.
- Prototyping atomic operations like rate limiters or distributed locks before deploying to production.
Sponsored
More from Caching & Redis
- Cache Asides in Python with a Read-Through Loader easy
- Cache Data in Redis with Python easy
- Cache Penetration Null Object Mock in Python medium
- Cache Stampede Prevention with SingleFlight in Python medium
- Cache Warming with Python: Preload Hot Keys easy
- Coalescing duplicate in-flight requests: one shared result for concurrent callers hard
Keep learning
Related tutorials and quizzes for this topic.