Skip to content

Instantly share code, notes, and snippets.

@Munawwar
Last active July 12, 2021 16:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Munawwar/78db6bae5212afef48380c978bd27e47 to your computer and use it in GitHub Desktop.
Save Munawwar/78db6bae5212afef48380c978bd27e47 to your computer and use it in GitHub Desktop.
Redis concurrency control
const redis = require('redis');
const bluebird = require('bluebird');
const redisClient = redis.createClient({});
bluebird.promisifyAll(Object.getPrototypeOf(redisClient));
const luaScript = `
local newPayload = ARGV[1]
local newVersionStr, newData = ARGV[1]:match("^([0-9]+)|(.+)$")
local prevVal = redis.call("get", KEYS[1]) or nil
if prevVal == nil then
return redis.call("set", KEYS[1], "1|" .. newData)
end
local oldVersionStr, oldData = prevVal:match("^([0-9]+)|(.+)$")
local newVersion = tonumber(newVersionStr)
local oldVersion = tonumber(oldVersionStr)
-- check if version matches before writing
if oldVersion == (newVersion - 1) then
return redis.call('set', KEYS[1], newPayload)
else
return nil
end
`;
(async () => {
const results = await Promise.all([
redisClient.evalAsync(luaScript, 1, 'cc', '1|{a: 1}'),
redisClient.evalAsync(luaScript, 1, 'cc', '1|{b: 2}'),
redisClient.evalAsync(luaScript, 1, 'cc', '2|{b: 2}'),
]);
console.log(results); // potential output [ 'OK', null, 'OK' ]
console.log(await redisClient.getAsync('cc'));
console.log(await redisClient.delAsync('cc'));
})();
@arch1995
Copy link

What should we do when we get null?

@Munawwar
Copy link
Author

Munawwar commented Apr 12, 2021

When you get back null, either show error to user

OR if automatic merge it's possible,
fetch data again, merge new data with old, increase the version number and try to write again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment