tidis icon indicating copy to clipboard operation
tidis copied to clipboard

support redis eval script (lua)

Open Negashev opened this issue 5 years ago • 9 comments

Can we do support the EVAL command?

https://redis.io/commands/eval

Negashev avatar Mar 04 '19 14:03 Negashev

Something like https://github.com/yuin/gopher-lua#user-defined-types with load redis.call from golang

Negashev avatar Mar 04 '19 18:03 Negashev

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 avatar Mar 05 '19 01:03 yongman

@yongman Can I suggest PR?

Negashev avatar Mar 05 '19 06:03 Negashev

@Negashev Sure. Any PR will be welcomed and looking forward your PR. :-)

yongman avatar Mar 05 '19 06:03 yongman

@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

Negashev avatar Mar 05 '19 12:03 Negashev

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 avatar Mar 06 '19 00:03 yongman

@yongman Yes, I wrote about this) I just do not know how to call tidis.command

Negashev avatar Mar 06 '19 06:03 Negashev

@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 avatar Mar 06 '19 06:03 yongman

@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

Negashev avatar Mar 06 '19 07:03 Negashev

Please refer to https://github.com/tidb-incubator/tidis

yongman avatar Sep 20 '22 05:09 yongman