Redis: Atomic get and conditional set
Asked Answered
P

5

7

I'd like to perform an atomic GET in Redis, and if the value returned is equal to some expected value, I'd like to do a SET, but I want to chain all of this together as one atomic operation. (I'm trying to set a flag that indicates whether any process is writing data to disk, as only one process may be permitted to do so.)

Is it possible to accomplish this with Redis?

I have seen documentation on MULTI operations but I haven't seen conditional operations i MULTI operations. Any suggestions others can offer with this would be greatly appreciated!

Pleasure answered 12/5, 2018 at 17:59 Comment(3)
You can do it all redis-side with a Lua script: redis.io/commands/evalVerso
@ChrisTanner make this into an answer, and I'll upvoteBlackamoor
In case others also wish to create a write lock, this is great reading: redis.io/topics/distlockPleasure
V
6

You can do both the GET and set operations on the redis server itself using Lua scripts. They're atomic and allow you to add logic too.

Verso answered 12/5, 2018 at 23:53 Comment(2)
thanks for your thought. Do you happen to know of any examples of tutorials on simple Lua scripts in Redis that are good for beginners? I haven't worked with Lua before, so would like to start with some real basics...Pleasure
These two pages got me on the right track to implement something just like you're looking for redisgreen.net/blog/intro-to-lua-for-redis-programmers and compose.com/articles/a-quick-guide-to-redis-lua-scriptingTc
P
1

I ended up using redlock-py, an implementation of the redlock algorithm that the Redis docs recommend for creating write locks: https://redis.io/topics/distlock. The linked article is fantastic reading for anyone looking to create similar write locks in Redis.

Pleasure answered 14/5, 2018 at 23:12 Comment(0)
P
1

I came across this post looking for a similar type of function, but I didn't see any options that appealed to me. I opted instead to write a small module in Rust that provides this exact type of operation:

https://github.com/KennethWilke/redis-setif

With this module you would do this via:

SETIF <key> <expected> <new>
HSETIF <key> <field> <expected> <new>
Pennyweight answered 11/11, 2021 at 2:49 Comment(0)
B
0

redis-if - lua script for "conditional transactions". More convenient than WATCH + MULTY.

You can pass any combination of conditions & followed commands as json object:

const Redis = require('ioredis')

const redis = new Redis()

redis.defineCommand('transaction', { lua: require('redis-if').script, numberOfKeys: 0 })

await redis.set('custom-state', 'initialized')
await redis.set('custom-counter', 0)

// this call will change state and do another unrelated operation (increment) atomically
let success = await redis.transaction(JSON.stringify({
  if: [
    // apply changes only if this process has acquired a lock
    [ 'initialized', '==', [ 'sget', 'custom-state' ] ]
  ],
  exec: [
    [ 'set', 'custom-state', 'finished' ],
    [ 'incr', 'custom-counter' ]
  ]
}))

With this script we removed all custom scripting from our projects.

Betelgeuse answered 20/2, 2021 at 6:48 Comment(0)
L
0

You can do this by SET command, with these 2 arguments, which according to the docs here:

GET - return the old string stored at key, or nil if key did not exist.

NX - Only set the key if it does not already exist.

Since Redis doesn't execute any command while another command is running - you have the 2 operations in an atomic manner.

Laue answered 7/2, 2023 at 17:15 Comment(1)
important: Starting with Redis version 7.0.0: Allowed the NX and GET options to be used together.Laue

© 2022 - 2024 — McMap. All rights reserved.