tidis
tidis copied to clipboard
support redis eval script (lua)
Can we do support the EVAL command?
https://redis.io/commands/eval
Something like https://github.com/yuin/gopher-lua#user-defined-types
with load redis.call
from golang
Thanks your advise. Tidis will add executing lua script support in future maybe.
But the differents between redis
and tidis
is redis
is one thread storage engine and tidis
is just a computing layer, we can do some heavy compute and write result to tidis
which will be conflict with other transactions resulting commit failure after retry.
@yongman Can I suggest PR?
@Negashev Sure. Any PR will be welcomed and looking forward your PR. :-)
@yongman Okay i write some test with auto insert redis
interpritator
package main
import (
"github.com/go-redis/redis"
"github.com/yuin/gopher-lua"
"strconv"
)
const luaRedisTypeName = "redis"
// TODO
// simulate connect to tidis
// need understand tidis api
var redisClient = redis.NewClient(&redis.Options{
Addr: "localhost:6379", // tidis addr (for container can use localhost)
Password: "", // no password set
DB: 0, // use default DB
})
// Registers my redis type to given L.
func registerRedisType(L *lua.LState) {
mt := L.NewTypeMetatable(luaRedisTypeName)
L.SetGlobal("redis", mt)
// static attributes
L.SetField(mt, "call", L.NewFunction(newRedisCall))
}
// Constructor
func newRedisCall(L *lua.LState) int {
var rest []interface{}
// filter data from lua to redis command
for i := L.GetTop(); i >= 1; i-- {
lv := L.Get(i)
if lv.Type().String() == "string" {
rest = append([]interface{}{L.CheckString(i)}, rest...)
} else if lv.Type().String() == "number" {
num, _ := strconv.Atoi(L.CheckNumber(i).String())
rest = append([]interface{}{num}, rest...)
} else if lv.Type().String() == "boolean" {
rest = append([]interface{}{L.CheckBool(i)}, rest...)
} else {
rest = append([]interface{}{L.CheckString(i)}, rest...)
}
}
// redis call command
result, err := redisClient.Do(rest...).Result()
if err != nil {
println(err)
}
// return integer
in, ok := result.(int64)
if ok {
L.Push(lua.LNumber(in))
return 1
}
//return only on string
s, ok := result.(string)
if ok {
L.Push(lua.LString(s))
return 1
}
//return array of string
array, ok := result.([]interface{})
if ok {
for _, key := range array {
L.Push(lua.LString(key.(string)))
}
return len(array)
}
return 0
}
func main() {
L := lua.NewState()
defer L.Close()
registerRedisType(L)
if err := L.DoString(`
print(redis.call("PING"))
print(redis.call("SET", "key", 1452))
print(redis.call("GET", "key"))
print(redis.call("expire", "key", 10))
print(redis.call("KEYS", "*"))
print(redis.call("INFO"))
`); err != nil {
panic(err)
}
}
My code not better, byt it work
need in apped new method eval
to tidis
and fix connect to tidis in localhost by tidis api
Good work. I think it is not a good idea to call tidis with socket api in lua, it will have performance loss. Can we call tidis function to handle request in lua directly?
@yongman Yes, I wrote about this) I just do not know how to call tidis.command
@Negashev We can treat it as a fake client, and handle client request as usual.
The main client handle logic is located in tidis/client.go
.
You may have better understanding after reviewing it.
@yongman Sorry, tidis api too hard for me 😓 (I know golang is very bad)
i can ping handleRequest(req [][]byte) from tidis/client.go
- can't init tidis client in my code
- do not understand how to return the result from handleRequest
Please refer to https://github.com/tidb-incubator/tidis