MEDIUM: lua: don't call the GC as often when dealing with outgoing connections

In order to properly close connections established from Lua in case
a Lua context dies, the context currently automatically gets a flag
HLUA_MUST_GC set whenever an outgoing connection is used. This causes
the GC to be enforced on the context's death as well as on yield. First,
it does not appear necessary to do it when yielding, since if the
connections die they are already cleaned up. Second, the problem with
the flag is that even if a connection gets properly closed, the flag is
not removed and the GC continues to be called on the Lua context.

The impact on performance looks quite significant, as noticed and
diagnosed by Sadasiva Gujjarlapudi in the following thread:

  https://www.mail-archive.com/haproxy@formilux.org/msg35810.html

This patch changes the flag for a counter so that each created
connection increments it and each cleanly closed connection decrements
it. That way we know we have to call the GC on the context's death only
if the count is non-null. As reported in the thread above, the Lua
performance gain is now over 20% by doing this.

Thanks to Sada and Thierry for the design discussion and tests that
led to this solution.
This commit is contained in:
Willy Tarreau 2020-01-14 09:59:38 +01:00
parent 9a8ef7f51d
commit f31af9367e
2 changed files with 8 additions and 8 deletions

View File

@ -37,7 +37,7 @@ struct stream;
#define HLUA_WAKERESWR 0x00000004
#define HLUA_WAKEREQWR 0x00000008
#define HLUA_EXIT 0x00000010
#define HLUA_MUST_GC 0x00000020
/* unused: 0x00000020 */
#define HLUA_STOP 0x00000040
#define HLUA_F_AS_STRING 0x01
@ -93,6 +93,7 @@ struct hlua {
struct list com; /* The list head of the signals attached to this task. */
struct ebpt_node node;
struct hlua_consistency cons; /* Store data consistency check. */
int gc_count; /* number of items which need a GC */
};
/* This is a part of the list containing references to functions

View File

@ -905,6 +905,7 @@ int hlua_ctx_init(struct hlua *lua, struct task *task, int already_safe)
}
lua->Mref = LUA_REFNIL;
lua->flags = 0;
lua->gc_count = 0;
LIST_INIT(&lua->com);
lua->T = lua_newthread(gL.T);
if (!lua->T) {
@ -954,7 +955,7 @@ void hlua_ctx_destroy(struct hlua *lua)
* NOTE: maybe this action locks all the Lua threads untiml the en of
* the garbage collection.
*/
if (lua->flags & HLUA_MUST_GC) {
if (lua->gc_count) {
if (!SET_SAFE_LJMP(gL.T))
return;
lua_gc(gL.T, LUA_GCCOLLECT, 0);
@ -1194,11 +1195,6 @@ static enum hlua_exec hlua_ctx_resume(struct hlua *lua, int yield_allowed)
break;
}
/* This GC permits to destroy some object when a Lua timeout strikes. */
if (lua->flags & HLUA_MUST_GC &&
ret != HLUA_E_AGAIN)
lua_gc(lua->T, LUA_GCCOLLECT, 0);
switch (ret) {
case HLUA_E_AGAIN:
break;
@ -1699,6 +1695,7 @@ __LJMP static int hlua_socket_close_helper(lua_State *L)
struct hlua_socket *socket;
struct appctx *appctx;
struct xref *peer;
struct hlua *hlua = hlua_gethlua(L);
socket = MAY_LJMP(hlua_checksocket(L, 1));
@ -1711,6 +1708,8 @@ __LJMP static int hlua_socket_close_helper(lua_State *L)
peer = xref_get_peer_and_lock(&socket->xref);
if (!peer)
return 0;
hlua->gc_count--;
appctx = container_of(peer, struct appctx, ctx.hlua_cosocket.xref);
/* Set the flag which destroy the session. */
@ -2463,7 +2462,7 @@ __LJMP static int hlua_socket_connect(struct lua_State *L)
si_rx_endp_more(&s->si[0]);
appctx_wakeup(appctx);
hlua->flags |= HLUA_MUST_GC;
hlua->gc_count++;
if (!notification_new(&hlua->com, &appctx->ctx.hlua_cosocket.wake_on_write, hlua->task)) {
xref_unlock(&socket->xref, peer);