英文:
Calling lua script from redigo throwing error wrong number of args
问题
我正在尝试使用redigo在Redis上执行一个Lua脚本。我的代码如下所示:
package main
import (
"github.com/PuerkitoBio/redisc"
"github.com/garyburd/redigo/redis"
"log"
"time"
)
const script = `if redis.call("EXISTS", KEYS[1]) == 1 then
local keyvalues = redis.call("HGETALL", KEYS[1])
local a = {}
for i=2, table.getn(ARGV) do
a[i-1] = ARGV[i]
end
local res = redis.call("HMSET", KEYS[1], unpack(a))
redis.call("EXPIRE", KEYS[1], ARGV[1])
return keyvalues
else
return 2 -- "Key doesn't exists"
end`
func main() {
cluster := redisc.Cluster{
StartupNodes: []string{":30001", ":30002", ":30003", ":30004", ":30005", ":30006"},
DialOptions: []redis.DialOption{redis.DialConnectTimeout(5 * time.Second)},
CreatePool: createPool,
}
defer cluster.Close()
// initialize its mapping
if err := cluster.Refresh(); err != nil {
log.Fatalf("Refresh failed: %v", err)
}
// grab a connection from the pool
conn := cluster.Get()
defer cluster.Close()
retryConn, errRe := redisc.RetryConn(conn, 3, 1*time.Millisecond)
if errRe != nil {
log.Println("Failed to get retry connection " + errRe.Error())
return
}
rScript := redis.NewScript(1, script)
argv := make([]string, 5)
argv[0] = "30000"
argv[1] = "SSF_lastAccessedDate"
argv[2] = "1481627386"
argv[3] = "SSF_expiryDate"
argv[4] = "2481657386"
reply, errS := rScript.Do(retryConn, "JJNb324a680c35d11e6a1123c15c2d271f21481871788G", argv)
if errS != nil {
log.Println("Error in executing script " + errS.Error())
} else {
log.Printf("Result %+v", reply)
}
}
func createPool(addr string, opts ...redis.DialOption) (*redis.Pool, error) {
return &redis.Pool{
MaxIdle: 100,
MaxActive: 4000,
IdleTimeout: time.Minute,
Dial: func() (redis.Conn, error) {
return redis.Dial("tcp", addr, opts...)
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
return nil
}
_, err := c.Do("PING")
return err
},
}, nil
}
但是在执行代码时,它会抛出以下错误:
2016/12/16 12:52:32 Error in executing script ERR Error running script (call to f_5a127779e5c1c2daa0b636d9b02f6b570a9f7f13): @user_script:7: @user_script: 7: Wrong number of args calling Redis command From Lua script
有人可以告诉我出了什么问题,以及如何使其工作吗?
环境:
- Go - 1.7.4
编辑
根据答案的建议,我将调用修改为:
rScript.Do(retryConn, "JJNb324a680c35d11e6a1123c15c2d271f21481871788G", argv[0], argv[1], argv[2], argv[3], argv[4])
但是它的输出是:
2016/12/16 21:39:51 Result [[116 121 112 101] [50] [100 97 116 97] [72 101 108 108 111] [120] [50] [83 83 70 95 99 114 101 97 116 105 111 110 68 97 116 101] [49 52 56 49 56 55 49 55 56 56] [83 83 70 95 108 97 115 116 77 111 100 105 102 105 101 100 68 97 116 101] [49 52 56 49 56 55 49 55 56 56] [83 83 70 95 101 120 112 105 114 121 68 97 116 101] [50 52 56 49 54 53 55 51 56 54] [83 83 70 95 108 97 115 116 65 99 99 101 115 115 101 100 68 97 116 101] [49 52 56 49 54 50 55 51 56 54]]
但是使用redis-cli
命令,我得到的输出是:
1) "type"
2) "2"
3) "data"
4) "Hello"
5) "x"
6) "2"
7) "SSF_creationDate"
8) "1481871788"
9) "SSF_lastModifiedDate"
10) "1481871788"
11) "SSF_expiryDate"
12) "2481657386"
13) "SSF_lastAccessedDate"
14) "1481627386"
英文:
I am trying to use redigo to execute a lua script on redis. My code looks like below:-
package main
import (
"github.com/PuerkitoBio/redisc"
"github.com/garyburd/redigo/redis"
"log"
"time"
)
const script = `if redis.call("EXISTS", KEYS[1]) == 1 then
local keyvalues = redis.call("HGETALL", KEYS[1])
local a = {}
for i=2, table.getn(ARGV) do
a[i-1] = ARGV[i]
end
local res = redis.call("HMSET", KEYS[1], unpack(a))
redis.call("EXPIRE", KEYS[1], ARGV[1])
return keyvalues
else
return 2 -- "Key doesn't exists"
end`
func main() {
cluster := redisc.Cluster{
StartupNodes: []string{":30001", ":30002", ":30003", ":30004", ":30005", ":30006"},
DialOptions: []redis.DialOption{redis.DialConnectTimeout(5 * time.Second)},
CreatePool: createPool,
}
defer cluster.Close()
// initialize its mapping
if err := cluster.Refresh(); err != nil {
log.Fatalf("Refresh failed: %v", err)
}
// grab a connection from the pool
conn := cluster.Get()
defer cluster.Close()
retryConn, errRe := redisc.RetryConn(conn, 3, 1*time.Millisecond)
if errRe != nil {
log.Println("Failed to get retry connection " + errRe.Error())
return
}
rScript := redis.NewScript(1, script)
argv := make([]string, 5)
argv[0] = "30000"
argv[1] = "SSF_lastAccessedDate"
argv[2] = "1481627386"
argv[3] = "SSF_expiryDate"
argv[4] = "2481657386"
reply, errS := rScript.Do(retryConn, "JJNb324a680c35d11e6a1123c15c2d271f21481871788G", argv)
if errS != nil {
log.Println("Error in executing script " + errS.Error())
} else {
log.Printf("Result %+v", reply)
}
}
func createPool(addr string, opts ...redis.DialOption) (*redis.Pool, error) {
return &redis.Pool{
MaxIdle: 100,
MaxActive: 4000,
IdleTimeout: time.Minute,
Dial: func() (redis.Conn, error) {
return redis.Dial("tcp", addr, opts...)
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
return nil
}
_, err := c.Do("PING")
return err
},
}, nil
}
But on executing the code it is throwing the error:-
2016/12/16 12:52:32 Error in executing script ERR Error running script (call to f_5a127779e5c1c2daa0b636d9b02f6b570a9f7f13): @user_script:7: @user_script: 7: Wrong number of args calling Redis command From Lua script
Can someone let me know what is going wrong & how to make this work?
Environment:-
- Go - 1.7.4
EDIT
As suggested in the answers I modified the call as:-
rScript.Do(retryConn, "JJNb324a680c35d11e6a1123c15c2d271f21481871788G", argv[0], argv[1], argv[2], argv[3], argv[4])
But it is giving the output as:-
2016/12/16 21:39:51 Result [[116 121 112 101] [50] [100 97 116 97] [72 101 108 108 111] [120] [50] [83 83 70 95 99 114 101 97 116 105 111 110 68 97 116 101] [49 52 56 49 56 55 49 55 56 56] [83 83 70 95 108 97 115 116 77 111 100 105 102 105 101 100 68 97 116 101] [49 52 56 49 56 55 49 55 56 56] [83 83 70 95 101 120 112 105 114 121 68 97 116 101] [50 52 56 49 54 53 55 51 56 54] [83 83 70 95 108 97 115 116 65 99 99 101 115 115 101 100 68 97 116 101] [49 52 56 49 54 50 55 51 56 54]]
But from redis-cli
I am getting the output as:-
1) "type"
2) "2"
3) "data"
4) "Hello"
5) "x"
6) "2"
7) "SSF_creationDate"
8) "1481871788"
9) "SSF_lastModifiedDate"
10) "1481871788"
11) "SSF_expiryDate"
12) "2481657386"
13) "SSF_lastAccessedDate"
14) "1481627386"
答案1
得分: 1
你没有正确使用Do
方法,如果你想在脚本中使用变量,必须将每个变量单独传递给它:
rScript := redis.NewScript(1, script)
argv := make([]string, 6)
argv[0] = "JJNb324a680c35d11e6a1123c15c2d271f21481871788G" // the key
argv[1] = "30000"
argv[2] = "SSF_lastAccessedDate"
argv[3] = "1481627386"
argv[4] = "SSF_expiryDate"
argv[5] = "2481657386"
reply, err := rScript.Do(con, argv...)
if err != nil {
log.Println("Error in executing script " + err.Error())
} else {
log.Printf("Result %v\n", reply)
// error if not return a map
if data, err := redis.StringMap(reply, nil); err != nil {
// error if not return an int
if val, err := redis.Int64(reply, nil); err != nil {
log.Printf("Error %v\n", err)
} else {
log.Printf("Result %v\n", val)
}
} else {
log.Printf("Result %v\n", data)
}
}
英文:
You're not using Do
method correctly, you must to give it every variable separately if you want to use them in the script:
rScript := redis.NewScript(1, script)
argv := make([]string, 6)
argv[0] = "JJNb324a680c35d11e6a1123c15c2d271f21481871788G" // the key
argv[1] = "30000"
argv[2] = "SSF_lastAccessedDate"
argv[3] = "1481627386"
argv[4] = "SSF_expiryDate"
argv[5] = "2481657386"
reply, err := rScript.Do(con, argv...)
if err != nil {
log.Println("Error in executing script " + err.Error())
} else {
log.Printf("Result %v\n", reply)
// error if not return a map
if data, err := redis.StringMap(reply, nil); err != nil {
// error if not return an int
if val, err := redis.Int64(reply, nil); err != nil {
log.Printf("Error %v\n", err)
} else {
log.Printf("Result %v\n", val)
}
} else {
log.Printf("Result %v\n", data)
}
}
答案2
得分: 1
根据错误信息,错误出现在以下代码行:
local res = redis.call("HMSET", KEYS[1], unpack(a))
问题在于应用程序将两个参数传递给脚本,即键和将argv
转换为字符串。调用unpack(a)
只返回一个值。
修复方法是将每个参数单独传递给脚本:
reply, errS := rScript.Do(retryConn,
"JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
"30000",
"SSF_lastAccessedDate", "1481627386",
"SSF_expiryDate", "2481657386")
我建议使用包级别变量初始化脚本,这样脚本文本的哈希值只需计算一次,而不是在每次使用脚本时都计算:
var rScript = redis.NewScript(1, `if redis.call("EXISTS", KEYS[1]) == 1 then
local keyvalues = redis.call("HGETALL", KEYS[1])
local a = {}
for i=2, table.getn(ARGV) do
a[i-1] = ARGV[i]
end
local res = redis.call("HMSET", KEYS[1], unpack(a))
redis.call("EXPIRE", KEYS[1], ARGV[1])
return keyvalues
else
return 2 -- "Key doesn't exist"
end`)
并在main()
函数中使用该脚本:
func main() {
...
reply, errS := rScript.Do(retryConn,
"JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
"30000",
"SSF_lastAccessedDate", "1481627386",
"SSF_expiryDate", "2481657386")
...
}
此外,使用table.remove
可以简化脚本:
var rScript = redis.NewScript(1, `if redis.call("EXISTS", KEYS[1]) == 1 then
local keyvalues = redis.call("HGETALL", KEYS[1])
local expires = table.remove(ARGV, 1)
local res = redis.call("HMSET", KEYS[1], unpack(ARGV))
redis.call("EXPIRE", KEYS[1], expires)
return keyvalues
else
return 2 -- "Key doesn't exist"
end`)
使用redis.Strings将从服务器返回的[]byte切片转换为字符串切片:
reply, errS := redis.Strings(rScript.Do(retryConn,
"JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
"30000",
"SSF_lastAccessedDate", "1481627386",
"SSF_expiryDate", "2481657386"))
打印reply
将得到您期望的结果。
英文:
According to the error message, the error is on this line:
local res = redis.call("HMSET", KEYS[1], unpack(a))
The issue is that the application is passing two arguments to the script, the key and argv
converted to a string. The call unpack(a)
yields a single value.
The fix is to pass each argument separately to the script:
reply, errS := rScript.Do(retryConn,
"JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
"30000",
"SSF_lastAccessedDate", "1481627386",
"SSF_expiryDate", "2481657386")
I recommend initializing a package level variable with the script so the hash of the script text is computed once instead of on every use of the script:
var rScript = redis.NewScript(1, `if redis.call("EXISTS", KEYS[1]) == 1 then
local keyvalues = redis.call("HGETALL", KEYS[1])
local a = {}
for i=2, table.getn(ARGV) do
a[i-1] = ARGV[i]
end
local res = redis.call("HMSET", KEYS[1], unpack(a))
redis.call("EXPIRE", KEYS[1], ARGV[1])
return keyvalues
else
return 2 -- "Key doesn't exists"
end`)
and use this script in main()
:
func main() {
...
reply, errS := rScript.Do(retryConn,
"JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
"30000",
"SSF_lastAccessedDate", "1481627386",
"SSF_expiryDate", "2481657386")
...
}
Also, use table.remove to simplify the script:
var rScript = redis.NewScript(1, `if redis.call("EXISTS", KEYS[1]) == 1 then
local keyvalues = redis.call("HGETALL", KEYS[1])
local expires = table.remove(ARGV, 1)
local res = redis.call("HMSET", KEYS[1], unpack(ARGV))
redis.call("EXPIRE", KEYS[1], expires)
return keyvalues
else
return 2 -- "Key doesn't exists"
end`)
Use redis.Strings to convert the slice of []byte returned from the server to a slice of strings:
reply, errS := redis.Strings(rScript.Do(retryConn,
"JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
"30000",
"SSF_lastAccessedDate", "1481627386",
"SSF_expiryDate", "2481657386"))
Printing this reply
will give the result you are expecting.
答案3
得分: 0
你应该使用args[1]、args[2]、args[3]、args[4]、args[5]
,而不是args[0]、args[1]、args[2]、args[3]、args[4]
。
英文:
You should begin use
args[1],args[2],args[3],args[4],args[5]
instead of
args[0],args[1],args[2],args[3],args[4]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论