Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Illustrating the difference between redis.call() and redis.pcall()

Tags:

redis

eval

lua

I tried executing the below erroneous eval command to understand the difference between redis.call() and redis.pcall()

eval "return redis.call(ARGV[2],KEYS[1])" 1 key get   
eval "return redis.pcall(ARGV[2],KEYS[1])" 1 key get

In both the cases, i got the error below,

(error) Lua redis() command arguments must be strings or integers

This error doesn't convey the difference between redis.call() and redis.pcall() as illustrated by the documentation which says

" redis.call() is similar to redis.pcall(), the only difference is that if a Redis command call will result into an error, redis.call() will raise a Lua error that in turn will force EVAL to return an error to the command caller, while redis.pcall will trap the error returning a Lua table representing the error."

So according to the documentation , in the case of using redis.pcall() the error should be trapped , right! In that case why are both the errors same?? If i have misconstrued the difference, it would be better if someone could clearly illustrate the difference between the commands!!

like image 940
Aarish Ramesh Avatar asked Dec 18 '13 12:12

Aarish Ramesh


2 Answers

This is a tricky case because in you example the command does not generate the error, you are using redis.call and redis.pcall incorrectly (because ARGV[2] is nil like the error message is telling you). So in both cases the error is not recovered.

Here is an example where the command actually fails and you can see the difference:

redis 127.0.0.1:6379> set foo bar
OK
redis 127.0.0.1:6379> eval 'redis.call("hget","foo","bar")' 0
(error) ERR Error running script (call to f_9e6d82f0740926e0a70775430bda59a54d4e0664): ERR Operation against a key holding the wrong kind of value
redis 127.0.0.1:6379> eval 'redis.pcall("hget","foo","bar")' 0
(nil)

However, you may notice that I did not return the result of pcall, so the script returns nil. What if I return the result of an erroneous command?

redis 127.0.0.1:6379> eval 'return redis.call("hget","foo","bar")' 0
(error) ERR Error running script (call to f_d0a8dce7264708876edf262052788fc90a8e8325): ERR Operation against a key holding the wrong kind of value
redis 127.0.0.1:6379> eval 'return redis.pcall("hget","foo","bar")' 0
(error) ERR Operation against a key holding the wrong kind of value

With call nothing changes because the error (think of it like an exception in other languages - Java, Python, etc) is thrown before the function has a chance to return anyway.

With pcall though, the function call returns a table with a single err field which is converted to an "Error reply" by Redis, so you do not see it. How can you check that? Linearize it!

redis 127.0.0.1:6379> eval 'local t = redis.pcall("hget","foo","bar"); local r = {type(t)}; for k,v in pairs(t) do r[#r+1] = k; r[#r+1] = v; end; return r' 0
1) "table"
2) "err"
3) "ERR Operation against a key holding the wrong kind of value"
like image 84
catwell Avatar answered Oct 11 '22 05:10

catwell


It fails because Redis cannot perform the call or pcall command. I mean it fails before the actual Redis command (here, it is a get command) is executed. pcall will catch errors during the execution of the Redis command, not during the execution of pcall itself.

Let's modify your input so that that the Redis command fails (and not the redis.call command itself).

> EVAL "return redis.call(ARGV[1],KEYS[1])" 1 key get
"100"

> EVAL "return redis.call(ARGV[1],KEYS[1])" 1 key born_to_fail
(error) ERR Error running script (call to f_2673dc91ae540aa65dedd262a952d5338e330b37): @user_script:1: @user_script: 1: Unknown Redis command called from Lua script

> EVAL "return redis.pcall(ARGV[1],KEYS[1])" 1 key born_to_fail
(error) @user_script: 1: Unknown Redis command called from Lua script

You can see on the second call the Redis error result in a Lua error.

On the third call, pcall is used instead, so the result is not anymore a Lua error, but simply an output string containing the text of the error.

like image 25
Didier Spezia Avatar answered Oct 11 '22 06:10

Didier Spezia