help translating this pseudo lua script

141 views
Skip to first unread message

S Ahmed

unread,
Jan 1, 2014, 4:56:05 PM1/1/14
to redi...@googlegroups.com
Hello,

Excited how I can reduce my redis calls using lua, hoping someone could help me translate what I have so far:


KEYS[1] = "somevaluekey"
KEYS[2] = "sometoherkey"
ARGS[1] = 100

local m1, m2, m3 = redis.call("HMGET", KEYS[1], "mem1", "mem2", "mem3")
if(m1 > 0)
  redis.call("ZINCBY", m1, 1, KEYS[1])
  if (m2 > 0)
    local counter = redis.call("INCR", KEYS[2])
    if ( counter > ARGS[1] )
      return 404
    else
      return 200
    end
  end
  return 405
end
else
  return 406
end

Pierre Chapuis

unread,
Jan 2, 2014, 7:08:18 AM1/2/14
to redi...@googlegroups.com
 
I will do this step by step.

1) Convert what you have to valid Lua syntax:

local m1, m2, m3 = redis.call("HMGET", KEYS[1], "mem1", "mem2", "mem3")
if m1 > 0 then

  redis.call("ZINCBY", m1, 1, KEYS[1])
  if m2 > 0 then

    local counter = redis.call("INCR", KEYS[2])
    if counter > ARGS[1] then

      return 404
    else
      return 200
    end
  end
  return 405
else
  return 406
end

2) Simplify the logic (note: you never used m3...):
 
local m1, m2 = redis.call("HMGET", KEYS[1], "mem1", "mem2")
if m1 <= 0 then return 406 end

redis.call("ZINCBY", m1, 1, KEYS[1])
if m2 <= 0 then return 405 end

local counter = redis.call("INCR", KEYS[2])
return (counter > ARGS[1]) and 404 or 200

3) Fix the bugs.

  3.a) HMGET returns a table of strings or nil-s, not multiple numbers.

local m = redis.call("HMGET", KEYS[1], "mem1", "mem2")
local m1, m2 = tonumber(m[1] or 0), tonumber(m[2] or 0)

  3.b) It is ZINCRBY, not ZINCBY, and I suspect you are not passing the arguments you want. You probably want something like this instead:

redis.call("ZINCRBY", KEYS[1], 1, "mem1")

  3.c) You need to convert the argument to a number as well, it is always passed as a string:

return (counter > tonumber(ARGS[1])) and 404 or 200

4) Finally here is the "fixed" script. Of course I cannot fix your logic since I do not know what you are trying to do exactly...

local m = redis.call("HMGET", KEYS[1], "mem1", "mem2")
local m1, m2 = tonumber(m[1] or 0), tonumber(m[2] or 0)
if m1 <= 0 then return 406 end
redis.call("ZINCRBY", KEYS[1], 1, "mem1")
if m2 <= 0 then return 405 end

local counter = redis.call("INCR", KEYS[2])
return (counter > tonumber(ARGS[1])) and 404 or 200

--
Pierre Chapuis

S Ahmed

unread,
Jan 2, 2014, 9:38:12 PM1/2/14
to redi...@googlegroups.com
Great thanks, testing it out now.

Is the last line correct?  return (counter > tonumber(ARGS[1])) and 404 or 200

return (...) and 404 or 200




--
You received this message because you are subscribed to the Google Groups "Redis DB" group.
To unsubscribe from this group and stop receiving emails from it, send an email to redis-db+u...@googlegroups.com.
To post to this group, send email to redi...@googlegroups.com.
Visit this group at http://groups.google.com/group/redis-db.
For more options, visit https://groups.google.com/groups/opt_out.

Mark Martinec

unread,
Jan 3, 2014, 5:52:22 AM1/3/14
to redi...@googlegroups.com
> Is the last line correct?
> return (counter > tonumber(ARGS[1])) and 404 or 200
>
> return (...) and 404 or 200

Yes, looks correct.

The "a and b or c" is a commonly used idiom in
Lua - which lacks a ternary operator a ? b : c,
but only when it can be ensured that b is always true
(which in this case it is).

Mark

Pierre Chapuis

unread,
Jan 3, 2014, 11:36:20 AM1/3/14
to redi...@googlegroups.com, mark.m...@ijs.si

 Yes, I should have explained that...

First: in Lua, everything that is not `false` or `nil` is true.

Then, the line looks like this: `return x and 404 or 200`. It works like this:

If x is true, then (x and 404) is `404`, so `(x and 404 or 200)` is `(404 or 200)` which is `404`.

If x is false, then (x and 404) is false as well, and ([something false] or 200) is 200.

So this line is equivalent to `if x then return 404 else return 200 end` (you can write it this way if you find it clearer).

S Ahmed

unread,
Mar 12, 2014, 11:11:15 PM3/12/14
to redi...@googlegroups.com
Hi Pierre,

So I finally got around to this again, thanks for your help.

You helped me out and we ended up with this:

local m = redis.call("HMGET", KEYS[1], "mem1", "mem2")
local m1, m2 = tonumber(m[1] or 0), tonumber(m[2] or 0)
if m1 <= 0 then return 406 end
redis.call("ZINCRBY", KEYS[1], 1, "mem1")
if m2 <= 0 then return 405 end

local counter = redis.call("INCR", KEYS[2])
return (counter > tonumber(ARGS[1])) and 404 or 200


The problem I have is I want a fixed return result from this script, so I always want a return for:

m, m1, m2 and counter.

So instead of returning 406 or 405 I would always return a value for all of those variables (with -1 or something if they don't get set maybe?).

I would then have to wrap the other things in 'if' statements since they won't get called like:

if m1 > 0 then
  redis.call("ZINCRBY", KEYS[1], 1, "mem1")
  if m2 > 0 then 
    local counter = redis.call("INCR", KEYS[2])
    return (counter > tonumber(ARGS[1])) and 404 or 200
  end
end

Does that make sense?
I guess I have to remove the 'return ( counter > ....' statement and return all the variables at the end right?  Is this a clean approach?














--

S Ahmed

unread,
Apr 3, 2014, 5:34:10 PM4/3/14
to redi...@googlegroups.com
hoping someone could help me with this.  The idea is to get the same # of variables back to make it easier to handle on the app side of things.

Josiah Carlson

unread,
Apr 3, 2014, 6:11:45 PM4/3/14
to redi...@googlegroups.com
Use a table! Feel free to remove entries from the table that you don't actually need.

local m = redis.call("HMGET", KEYS[1], "mem1", "mem2")
local m1, m2 = tonumber(m[1] or 0), tonumber(m[2] or 0)
local results = {m1, m2, -1, -1}
if m1 <= 0 then
    results[4] = 406
    return results
end
-- you had ZINCRBY here, which wouldn't work with KEYS[1], which is a HASH
redis.call("HINCRBY", KEYS[1], 1, "mem1")
if m2 <= 0 then
    results[4] = 405
    return results
end
local counter = tonumber(redis.call("INCR", KEYS[2]))
results[3] = counter
results[4] = (counter > tonumber(ARGS[1])) and 404 or 200
return results



For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages