valkey-io / valkey

A flexible distributed key-value datastore that is optimized for caching and other realtime workloads.
https://valkey.io
Other
17.23k stars 649 forks source link

[NEW] Enhanced Function API: off-thread JSON/MsgPack decoding of args and encoding of response #819

Open thx01138 opened 3 months ago

thx01138 commented 3 months ago

The problem/use-case that the feature addresses

The current way of invoking functions with an ordered list of args introduces tight coupling between the client code and the function definition. One way to work around this is to use JSON or MsgPack [from the client's perspective] to encode the args and decode the response. However, this requires the lua function to decode the args and encode the response which takes time on the main event loop thread.

Description of the feature Introduce a way to declare input/output encoding on a function so that redis will do the request arg decoding and response encoding off the main thread, just the way i/o is done off-thread.

Alternatives you've considered

I've looked for similar feature requests but haven't found any.

Additional information Benefit: increased server throughput as the event loop is not occupied with this en/decoding.

thx01138 commented 3 months ago

Impractical example here but it should demonstrate the benefit:

Instead of

redis.register_function({
    function_name = 'my_fun_function',
    callback = function(keys, args)
        local req = cmsgpack.decode(args[1])
        local resp = []
        if req.isOption1 then
          resp['option1']=redis_call('GET', keys[1]) 
        else
          resp['option2']=redis_call('GET', keys[2])
        end
        return cjson.encode(resp)
    end
})

we could do this:

redis.register_function({
    function_name = 'my_fun_function',
    arg_encoding = 'msgpack',
    response_encoding = 'json',
    callback = function(keys, args)
        local req = args[1]
        local resp = []
        if req.isOption1 then
          resp['option1']=redis_call('GET', keys[1]) 
        else
          resp['option2']=redis_call('GET', keys[2])
        end
        return resp
    end
})