Reputation: 42644
I have below code in nodejs
:
const decrease = async (userId, points) => {
const user = await redisClient.hgetall(userId);
if(user.points - points >= 0) {
await redisClient.hset(userId, userId, user.points - points);
}
}
since async/await
is not blocking the execution, if there are multiple requests for the same userId, the code is not running as atomically. That means the user points may be decreased multiple times even there is not enough point left on users account. How can I make the method run atomically?
I have checked redis multi
command and it works for multiple redis statements. But in my case, I need to calculate the user points which is not part of redis command. So how to make them run as an atomic function.
I also read the INCR pattern: https://redis.io/commands/incr But it doesn't seem to fix my issue. The patterns listed there need to work with expire which I don't have such requirement to give a specific timeout value.
Upvotes: 4
Views: 2119
Reputation: 50112
Use the power of (Redis) server-side Lua scripts by calling EVAL
. It should probably look something like this:
const lua = `
local p = redis.call('HGET',KEYS[1],'points')
local d = p - ARGV[1]
if d >= 0 then
redis.call('HSET', KEYS[1], 'points', d)
end`
const decrease = async (userId, points) => {
await redisClient.eval(lua, 1, userId, points);
}
Upvotes: 3