Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can redis pipeline multiple commands that depend on previous ones?

I'm very very new to redis and still playing around with it. I want to test to see if its relevant to my project but I'm not sure about a specific command I'm running. The users on SO have got me convinced of the performance benefits of using pipelines and transactions so I thought I'd ask how to do this.

Basically I have two statements that I just want to issue and not have to wait for the result(seems like a good candidate for pipe lining. It looks like this:

Does valueX exist?
If it does insert valueY

Its pretty simple but so far all the ways I have been looking into it seem to wait for a response for if ValueX exists, and because I'm doing over a billion loops of my program it grinds it to a halt.

Is this possible? If it helps I'm using Java but haven't settled on which client library(jedis or jredis, still testing). I'm actually not even fully settled on redis but leaning very heavily towards it(seems good for what I'm doing speed wise), so any suggestions are acceptable.

like image 675
Lostsoul Avatar asked Mar 08 '12 04:03

Lostsoul


2 Answers

No, it is not possible for the moment to accomplish such a thing. What you seek is a feature missing for the moment, but it will be available with the 2.6 version of Redis. It's called LUA scripting. You can execute server commands that are dependent of previous commands, all in one, without the need to fetch them at the client. For more details see here.

like image 150
hymloth Avatar answered Oct 10 '22 07:10

hymloth


Redis does not supports that directly, but this is very often demand in many cases. More generalized "atomic" pattern is:

Check multiple conditions
If all satisfied, run multiple commands

This can be acheived via simple lua script

-- Validate conditions and exec commands if ok

local params = cjson.decode(ARGV[1])

-- Check conditions
for __, check in pairs(params["if"]) do
    if #check == 2 then
        if check[1] ~= redis.call(unpack(check[2])) then return 0 end
    elseif check[2] == "==" then
        if check[1] ~= redis.call(unpack(check[3])) then return 0 end
    elseif check[2] == "!=" then
        if check[1] == redis.call(unpack(check[3])) then return 0 end
    elseif check[2] == ">" then
        if check[1] <= redis.call(unpack(check[3])) then return 0 end
    elseif check[2] == "<" then
        if check[1] >= redis.call(unpack(check[3])) then return 0 end
    else
        error('invalid operator "'..tostring(check[2])..'" (expected "==", "!=", ">" or "<")')
    end
end

-- Eval redis commands
for __, exec in pairs(params["exec"]) do
    redis.call(unpack(exec))
end

return 1

Then transaction details can be passed as simple JSON.stringify(object):

{
  // Conditions. All must be satisfied
  if: [
    [ 'initialized', '==', [ 'sget', 'custom-state' ] ]
  ],
  // Commands to execute if all conditions are satisfied
  exec: [
    [ 'set', 'custom-state', 'finished' ],
    [ 'incr', 'custom-counter' ]
  ]
}

In many cases, such "conditional transactions" eliminate need of custom scripting.

See https://github.com/nodeca/redis-if for more samples / tests / src.

like image 28
Vitaly Avatar answered Oct 10 '22 09:10

Vitaly