Skip to content

Commit 0d944f9

Browse files
committed
net.box: explicitly forbid synchronous requests in triggers
Net.box triggers (on_connect, on_schema_reload) are executed by the net.box connection worker fiber so a request issued by a trigger callback can't be processed until the trigger returns execution to the net.box fiber. Currently, an attempt to issue a synchronous request from a net.box trigger leads to a silent hang of the connection, which is confusing. Let's instead raise an error until #7291 is implemented. We need to add the check to three places in the code: 1. luaT_netbox_wait_result for future:wait_result() 2. luaT_netbox_iterator_next for future:pairs() 3. conn._request for all synchronous requests. (We can't add the check to luaT_netbox_transport_perform_request, because conn._request may also call conn.wait_state, which would hang if called from on_connect or on_schema_reload trigger.) We also add an assertion to netbox_request_wait to ensure that we never wait for a request completion in the net.box worker fiber. Closes #5358 @TarantoolBot document Title: Synchronous requests are not allowed in net.box triggers An attempt to issue a synchronous request (e.g. `call`) from a net.box trigger (`on_connect`, `on_schema_reload`) now raises an error: "Synchronous requests are not allowed in net.box trigger" (Before #5358 was fixed, it silently hung.) Invoking an asynchronous request (see `is_async` option) is allowed, but the request will not be processed until the trigger returns and an attempt to wait for the request completion with `future:pairs()` or `future:wait_result()` will raise the same error.
1 parent e20e41d commit 0d944f9

File tree

4 files changed

+58
-0
lines changed

4 files changed

+58
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## bugfix/core
2+
3+
* Fixed a hang when a synchronous request is issued from a net.box `on_connect`
4+
or `on_schema_reload` trigger. Now, an error is raised instead (gh-5358).

src/box/lua/net_box.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,12 @@ netbox_request_complete(struct netbox_request *request)
415415
static inline bool
416416
netbox_request_wait(struct netbox_request *request, double *timeout)
417417
{
418+
/*
419+
* Waiting for a request completion in the net.box worker fiber
420+
* would result in a dead lock.
421+
*/
422+
assert(request->transport != NULL &&
423+
request->transport->worker != fiber());
418424
if (*timeout == 0)
419425
return false;
420426
double ts = ev_monotonic_now(loop());
@@ -1877,6 +1883,11 @@ luaT_netbox_request_wait_result(struct lua_State *L)
18771883
(timeout = lua_tonumber(L, 2)) < 0)
18781884
luaL_error(L, "Usage: future:wait_result(timeout)");
18791885
}
1886+
if (request->transport != NULL &&
1887+
request->transport->worker == fiber()) {
1888+
luaL_error(L, "Synchronous requests are not allowed in "
1889+
"net.box trigger");
1890+
}
18801891
while (!netbox_request_is_ready(request)) {
18811892
if (!netbox_request_wait(request, &timeout)) {
18821893
luaL_testcancel(L);
@@ -1927,6 +1938,11 @@ luaT_netbox_request_iterator_next(struct lua_State *L)
19271938
struct netbox_request *request = luaT_check_netbox_request(L, -1);
19281939
lua_rawgeti(L, 1, 2);
19291940
double timeout = lua_tonumber(L, -1);
1941+
if (request->transport != NULL &&
1942+
request->transport->worker == fiber()) {
1943+
luaL_error(L, "Synchronous requests are not allowed in "
1944+
"net.box trigger");
1945+
}
19301946
/* The second argument is the index of the last returned message. */
19311947
if (luaL_isnull(L, 2)) {
19321948
/* The previous call returned an error. */

src/box/lua/net_box.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ local function new_sm(uri, opts)
274274
local last_reconnect_error
275275
local remote = {host = host, port = port, opts = opts, state = 'initial'}
276276
local function callback(what, ...)
277+
if remote._fiber == nil then
278+
remote._fiber = fiber.self()
279+
end
277280
if what == 'state_changed' then
278281
local state, err = ...
279282
local was_connected = remote._is_connected
@@ -735,6 +738,9 @@ function remote_methods:_request(method, opts, format, stream_id, ...)
735738
on_push = on_push_sync_default
736739
end
737740
-- Execute synchronous request.
741+
if self._fiber == fiber.self() then
742+
error('Synchronous requests are not allowed in net.box trigger')
743+
end
738744
local timeout = deadline and max(0, deadline - fiber_clock())
739745
if self.state ~= 'active' then
740746
self:wait_state('active', timeout)

test/box-luatest/net_box_test.lua

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,35 @@ g.test_gc = function()
336336
t.assert_not(weak_refs.callback)
337337
t.assert_not(weak_refs.transport)
338338
end
339+
340+
--
341+
-- Checks that synchronous requests fail with an error in on_connect and
342+
-- on_schema_reload triggers (gh-5358).
343+
--
344+
g.test_sync_request_in_trigger = function()
345+
local c
346+
local ch = fiber.channel(100)
347+
local function trigger_cb()
348+
local status, err
349+
status, err = pcall(c.call, c, 'box.session.user', {timeout = 5})
350+
ch:put(status or tostring(err))
351+
local fut = c:call('box.session.user', {}, {is_async = true})
352+
status, err = pcall(fut.wait_result, fut, 5)
353+
ch:put(status or tostring(err))
354+
status, err = pcall(function() for _, _ in fut:pairs(5) do end end)
355+
ch:put(status or tostring(err))
356+
end
357+
local function check()
358+
local msg = 'Synchronous requests are not allowed in net.box trigger'
359+
t.assert_str_contains(ch:get(5), msg) -- conn:call
360+
t.assert_str_contains(ch:get(5), msg) -- fut:wait_result
361+
t.assert_str_contains(ch:get(5), msg) -- fut:pairs
362+
end
363+
c = net.connect(g.server.net_box_uri, {wait_connected = false})
364+
t.assert_equals(c.state, 'initial')
365+
c:on_connect(trigger_cb)
366+
c:on_schema_reload(trigger_cb)
367+
check() -- on_connect
368+
check() -- on_schema_reload
369+
c:close()
370+
end

0 commit comments

Comments
 (0)