The Problem
efx.read (item , key , {intention:"update", backoff:true}) .then ((result)=> doSomething(result)) .then ((result)=> efx.update (result.data.value , item, key , "post" , {intent:result.data.intent});
efx.read (item , key , {intention:"update", backoff:true}) .then ((result)=> doSomething(result) ) .then ((result)=> { return result.data.value ? efx.update (result.data.value , item , key , "post" , {intent:result.data.intent}) : efx.release (item , key , result.data.intent); });
- You can’t just blindly delete that item because if the lock timeout has happened in the meantime and your lock has expired, and someone else has taken out a new lock on that item, you’ll be deleting his lock, not yours.
- You could increase the scope of the intent key to include both the intent key and the content, but that would then require multiple operations when checking for a lock in the first place – which itself could lead to race conditions.
- You could read it first to check that the lock is still the one you think it is by checking the content, but in the period between checking and deleting, problem 1 might still happen.
The solution
// returns nil .. the thing didnt exist , -1 it existed but didnt match , 0 it failed to delete , 1 it deleted // use redis.remove_if_matches (keytomatch, contenttoexpect) redisIntent_.defineCommand('remove_if_matches', { numberOfKeys: 1, lua: `if redis.call("exists", KEYS[1]) == 1 then local data = redis.call ("GET", KEYS[1]) if (data == ARGV[1]) then return redis.call ("DEL", KEYS[1]) else return -1 end else return nil end` });
Then use it as a custom method added to ioredis.
redisIntent_.remove_if_matches(key, content);
Walkthrough
if redis.call("exists", KEYS[1]) == 1 then
assign the result of calling redis.get using that key to a local variable
local data = redis.call ("GET", KEYS[1])
If the data matches the expected content, delete it otherwise return -1 to indicate it was found but the contents have changed
if (data == ARGV[1]) then return redis.call ("DEL", KEYS[1]) else return -1 end
If the item doesn’t exist return null (nil in Lua)
else return nil end
And that’s a wrap on another example of how you can use Lua scripting to preserve atomicity in Redis.
For more like this, see React, redux, redis, material-UI and firebase.
Why not join our forum, follow the blog or follow me on Twitter to ensure you get updates when they are available.