Skip to content

Commit d2a0124

Browse files
lockerkyukhin
authored andcommitted
box: add takes_raw_args lua function option
Closes #3349 @TarantoolBot document Title: Document that takes_raw_args function option A new function option was added - `takes_raw_args`: ```lua box.schema.func.create('my_func', {takes_raw_args = true}) ``` If set for a Lua function, the functions arguments will be passed wrapped in a msgpack object (see `msgpack.object()`) when the function is called over IPROTO or by `box.func.NAME:call()`: ```lua local msgpack = require('msgpack') local my_func = function(mp) assert(msgpack.is_object(mp)) local args = mp:decode() -- array of arguments end ``` Using this option might improve performance in case a function forwards most of its arguments to another instance or writes them to a database, because it eliminates msgpack decoding in Lua.
1 parent 66f5368 commit d2a0124

File tree

7 files changed

+118
-4
lines changed

7 files changed

+118
-4
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## feature/core
2+
3+
* Added `takes_raw_args` Lua function option for wrapping arguments in
4+
`msgpack.object` to skip decoding (gh-3349).

src/box/func_def.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,21 @@ const char *func_aggregate_strs[] = {"none", "group"};
4040

4141
const struct func_opts func_opts_default = {
4242
/* .is_multikey = */ false,
43+
/* .takes_raw_args = */ false,
4344
};
4445

4546
const struct opt_def func_opts_reg[] = {
4647
OPT_DEF("is_multikey", OPT_BOOL, struct func_opts, is_multikey),
48+
OPT_DEF("takes_raw_args", OPT_BOOL, struct func_opts, takes_raw_args),
4749
};
4850

4951
int
5052
func_opts_cmp(struct func_opts *o1, struct func_opts *o2)
5153
{
5254
if (o1->is_multikey != o2->is_multikey)
5355
return o1->is_multikey - o2->is_multikey;
56+
if (o1->takes_raw_args != o2->takes_raw_args)
57+
return o1->takes_raw_args - o2->takes_raw_args;
5458
return 0;
5559
}
5660

src/box/func_def.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ struct func_opts {
6868
* packed in array.
6969
*/
7070
bool is_multikey;
71+
/**
72+
* True if the function expects a msgpack object for args.
73+
*/
74+
bool takes_raw_args;
7175
};
7276

7377
extern const struct func_opts func_opts_default;

src/box/lua/call.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,9 +341,22 @@ struct execute_lua_ctx {
341341
int lua_ref;
342342
const char *name;
343343
uint32_t name_len;
344+
bool takes_raw_args;
344345
struct port *args;
345346
};
346347

348+
static inline void
349+
push_lua_args(lua_State *L, struct execute_lua_ctx *ctx)
350+
{
351+
if (ctx->takes_raw_args) {
352+
uint32_t size;
353+
const char *data = port_get_msgpack(ctx->args, &size);
354+
luamp_push(L, data, data + size);
355+
} else {
356+
port_dump_lua(ctx->args, L, true);
357+
}
358+
}
359+
347360
/**
348361
* Find a lua function by name and execute it. Used for body-less
349362
* UDFs, which may not yet be defined when a function definition
@@ -367,7 +380,7 @@ execute_lua_call(lua_State *L)
367380

368381
/* Push the rest of args (a tuple). */
369382
int top = lua_gettop(L);
370-
port_dump_lua(ctx->args, L, true);
383+
push_lua_args(L, ctx);
371384
int arg_count = lua_gettop(L) - top;
372385

373386
lua_call(L, arg_count + oc - 1, LUA_MULTRET);
@@ -389,7 +402,7 @@ execute_lua_call_by_ref(lua_State *L)
389402

390403
/* Push the rest of args (a tuple). */
391404
int top = lua_gettop(L);
392-
port_dump_lua(ctx->args, L, true);
405+
push_lua_args(L, ctx);
393406
int arg_count = lua_gettop(L) - top;
394407

395408
lua_call(L, arg_count, LUA_MULTRET);
@@ -413,7 +426,7 @@ execute_lua_eval(lua_State *L)
413426

414427
/* Unpack arguments */
415428
int top = lua_gettop(L);
416-
port_dump_lua(ctx->args, L, true);
429+
push_lua_args(L, ctx);
417430
int arg_count = lua_gettop(L) - top;
418431

419432
/* Call compiled code */
@@ -661,6 +674,7 @@ box_lua_call(const char *name, uint32_t name_len,
661674
ctx.name = name;
662675
ctx.name_len = name_len;
663676
ctx.args = args;
677+
ctx.takes_raw_args = false;
664678
return box_process_lua(HANDLER_CALL, &ctx, ret);
665679
}
666680

@@ -672,6 +686,7 @@ box_lua_eval(const char *expr, uint32_t expr_len,
672686
ctx.name = expr;
673687
ctx.name_len = expr_len;
674688
ctx.args = args;
689+
ctx.takes_raw_args = false;
675690
return box_process_lua(HANDLER_EVAL, &ctx, ret);
676691
}
677692

@@ -853,7 +868,12 @@ func_lua_call(struct func *func, struct port *args, struct port *ret)
853868
{
854869
assert(func != NULL && func->def->language == FUNC_LANGUAGE_LUA);
855870
assert(func->vtab == &func_lua_vtab);
856-
return box_lua_call(func->def->name, func->def->name_len, args, ret);
871+
struct execute_lua_ctx ctx;
872+
ctx.name = func->def->name;
873+
ctx.name_len = func->def->name_len;
874+
ctx.args = args;
875+
ctx.takes_raw_args = func->def->opts.takes_raw_args;
876+
return box_process_lua(HANDLER_CALL, &ctx, ret);
857877
}
858878

859879
static struct func_vtab func_lua_vtab = {
@@ -888,6 +908,7 @@ func_persistent_lua_call(struct func *base, struct port *args, struct port *ret)
888908
struct execute_lua_ctx ctx;
889909
ctx.lua_ref = func->lua_ref;
890910
ctx.args = args;
911+
ctx.takes_raw_args = base->def->opts.takes_raw_args;
891912
return box_process_lua(HANDLER_CALL_BY_REF, &ctx, ret);
892913

893914
}
@@ -1023,6 +1044,9 @@ lbox_func_new(struct lua_State *L, struct func *func)
10231044
lua_pushstring(L, "is_multikey");
10241045
lua_pushboolean(L, func->def->opts.is_multikey);
10251046
lua_settable(L, top);
1047+
lua_pushstring(L, "takes_raw_args");
1048+
lua_pushboolean(L, func->def->opts.takes_raw_args);
1049+
lua_settable(L, top);
10261050
lua_pushstring(L, "is_sandboxed");
10271051
if (func->def->body != NULL)
10281052
lua_pushboolean(L, func->def->is_sandboxed);

src/box/lua/schema.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2601,6 +2601,7 @@ box.schema.func.create = function(name, opts)
26012601
is_deterministic = 'boolean',
26022602
is_sandboxed = 'boolean',
26032603
is_multikey = 'boolean',
2604+
takes_raw_args = 'boolean',
26042605
comment = 'string',
26052606
param_list = 'table', returns = 'string',
26062607
exports = 'table', opts = 'table' })
@@ -2625,6 +2626,9 @@ box.schema.func.create = function(name, opts)
26252626
if opts.is_multikey then
26262627
opts.opts.is_multikey = opts.is_multikey
26272628
end
2629+
if opts.takes_raw_args then
2630+
opts.opts.takes_raw_args = opts.takes_raw_args
2631+
end
26282632
_func:auto_increment{session.euid(), name, opts.setuid, opts.language,
26292633
opts.body, opts.routine_type, opts.param_list,
26302634
opts.returns, opts.aggregate, opts.sql_data_access,

test/box-luatest/func_test.lua

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
local net = require('net.box')
12
local server = require('test.luatest_helpers.server')
23
local t = require('luatest')
34
local g = t.group()
@@ -42,3 +43,73 @@ g.test_legacy_opts = function()
4243
return ret
4344
end))
4445
end
46+
47+
g.before_test('test_msgpack_object_args', function()
48+
local echo_code = "function(...) return ... end"
49+
local check_code = "function(o) return require('msgpack').is_object(o) end"
50+
local decode_code = "function(o) return o:decode() end"
51+
g.server:eval("echo = " .. echo_code)
52+
g.server:eval("echo_mp = " .. echo_code)
53+
g.server:eval("check = " .. check_code)
54+
g.server:eval("decode = " .. decode_code)
55+
g.server:exec(function(check_code, decode_code)
56+
box.schema.func.create('echo')
57+
box.schema.func.create('echo_mp', {takes_raw_args = true})
58+
box.schema.func.create('check', {takes_raw_args = true})
59+
box.schema.func.create('decode', {takes_raw_args = true})
60+
box.schema.func.create(
61+
'check_persistent', {body = check_code, takes_raw_args = true})
62+
box.schema.func.create(
63+
'decode_persistent', {body = decode_code, takes_raw_args = true})
64+
end, {check_code, decode_code})
65+
end)
66+
67+
g.after_test('test_msgpack_object_args', function()
68+
g.server:exec(function()
69+
box.schema.func.drop('echo')
70+
box.schema.func.drop('echo_mp')
71+
box.schema.func.drop('check')
72+
box.schema.func.drop('decode')
73+
box.schema.func.drop('check_persistent')
74+
box.schema.func.drop('decode_persistent')
75+
end)
76+
g.server:eval("echo = nil")
77+
g.server:eval("echo_mp = nil")
78+
g.server:eval("check = nil")
79+
g.server:eval("decode = nil")
80+
end)
81+
82+
g.test_msgpack_object_args = function()
83+
local args = {'foo', 'bar', {foo = 'bar'}}
84+
85+
-- remote call
86+
local c = net:connect(g.server.net_box_uri)
87+
t.assert_equals({c:call('echo', args)}, args)
88+
t.assert_equals(c:call('echo_mp', args), args)
89+
t.assert(c:call('check', args))
90+
t.assert_equals(c:call('decode', args), args)
91+
t.assert(c:call('check_persistent', args))
92+
t.assert_equals(c:call('decode_persistent', args), args)
93+
c:close()
94+
95+
-- local call
96+
local call = function(name, args)
97+
return g.server:exec(function(name, args)
98+
return box.func[name]:call(args)
99+
end, {name, args})
100+
end
101+
t.assert_equals({call('echo', args)}, args)
102+
t.assert_equals(call('echo_mp', args), args)
103+
t.assert(call('check', args))
104+
t.assert_equals(call('decode', args), args)
105+
t.assert(call('check_persistent', args))
106+
t.assert_equals(call('decode_persistent', args), args)
107+
108+
-- info
109+
t.assert_not(g.server:eval('return box.func.echo.takes_raw_args'))
110+
t.assert(g.server:eval('return box.func.echo_mp.takes_raw_args'))
111+
t.assert(g.server:eval('return box.func.check.takes_raw_args'))
112+
t.assert(g.server:eval('return box.func.decode.takes_raw_args'))
113+
t.assert(g.server:eval('return box.func.check_persistent.takes_raw_args'))
114+
t.assert(g.server:eval('return box.func.decode_persistent.takes_raw_args'))
115+
end

test/box/function1.result

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ box.func["function1.args"]
9898
lua: true
9999
sql: false
100100
id: 66
101+
takes_raw_args: false
101102
setuid: false
102103
is_multikey: false
103104
is_deterministic: false
@@ -589,6 +590,7 @@ func
589590
lua: true
590591
sql: false
591592
id: 66
593+
takes_raw_args: false
592594
setuid: false
593595
is_multikey: false
594596
is_deterministic: false
@@ -661,6 +663,7 @@ func
661663
lua: true
662664
sql: false
663665
id: 66
666+
takes_raw_args: false
664667
setuid: false
665668
is_multikey: false
666669
is_deterministic: false

0 commit comments

Comments
 (0)