I catch the bug. It affect all the versions of HAProxy embedding Lua.
It is fixed.

I join 2 main patches:

 - 0001*.patch: the bug fix, it should be backported
 - 0002*.patch: a nice cleanup which is useless to backport

And I join the backport for the 1.6 version because it is not trivial.

 - 0001-*.1.6.patch

Willy, can you merge these bugfix ?

Thierry


On Wed, 26 Apr 2017 07:51:04 -0400
Philip Seidel <[email protected]> wrote:

> Thierry --
> 
> Thanks for your help with this.  I tried running with that line commented
> out and I still see the same behavior during my testing.  Memory is still
> never reclaimed throughout the life of the haproxy process.
> 
> Thanks,
> 
> -Phil
> 
> On Wed, Apr 26, 2017 at 7:09 AM, <[email protected]> wrote:
> 
> > It seems that the memleak doesn't appear when if remove the line which
> > sets the "priv" context do nil.
> >
> > So, when I comment this line:
> >
> >    txn:set_priv(txnPrvState);
> >
> > The memleak disappear. Now if you want, you can apply this workaroud.
> > I try to understand the memleak.
> >
> > Thierry
> >
> >
> > On Wed, 26 Apr 2017 12:11:16 +0200
> > Thierry Fournier <[email protected]> wrote:
> >
> > > Hi,
> > >
> > > Thanks for the bug report. I can reproduce the bug.
> > > I will look for a fix.
> > >
> > > Thierry
> > >
> > >
> > > > On 26 Apr 2017, at 05:14, Philip Seidel <[email protected]>
> > wrote:
> > > >
> > > > Possible Lua Memory Leak?
> > > >
> > > > We are running HAProxy version 1.7.2 (also tested 1.7.5) and are
> > loading a Lua file which makes use of 2 actions.  The first action is
> > called on http-request and the second on http-response.  In the simple test
> > case we put together the request action stores some data in a table and
> > calls txn:set_priv to save the state for the transaction.  The response
> > action calls txn:get_priv in order to access the state that was set in the
> > request action.  All variables are set to nil once they are no longer
> > needed but it seems that no matter what kind of load HAProxy is receiving
> > we leak a little bit of memory with each request.  Eventually, response
> > times begin to increase to the point where health checks to HAProxy begin
> > to fail and the server is unresponsive.  We can take the instance out of
> > rotation and memory still doesn't get reclaimed despite all connections to
> > the frontend and backend being closed.  We do see garbage collections
> > happening but it never cleans up enough to stabi
> >  lize the instance.  The leak is easily reproducible using a local test
> > instance and JMeter.  The test uses 10 connections with keep-alive at
> > around 1500 r/s.  It takes only a few minutes to consume over 1.5G of
> > memory.  Any ideas as to what might be going on here?  Is there something
> > wrong with how we are attempting to integrate these LUA actions?  I am
> > happy to provide additional information if anyone is willing to assist with
> > this.  I have posted the configuration and other files on pastebin.
> > > >
> > > > Configuration - https://pastebin.com/SBtAEZ9R
> > > > Lua - https://pastebin.com/64Anbxm4
> > > >
> > >
> > >
> >
>From ea1dc9b8dcb0bcf340132402d23648a2c397a4b3 Mon Sep 17 00:00:00 2001
From: Thierry FOURNIER <[email protected]>
Date: Wed, 26 Apr 2017 14:25:58 +0200
Subject: [PATCH] BUG/MEDIUM: lua: memory leak

The priv context is not cleaned when we set a new priv context.
This is caused by a stupid swap between two parameter of the
luaL_unref() function.

workaround: use set_priv only once when we process a stream.

This patch should be backported in version 1.7 and 1.6
---
 src/hlua.c |   36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/src/hlua.c b/src/hlua.c
index 4e35785..d276e3a 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -3615,6 +3615,40 @@ static int hlua_applet_http_new(lua_State *L, struct appctx *ctx)
 	return 1;
 }
 
+<<<<<<< HEAD
+=======
+__LJMP static int hlua_applet_http_set_priv(lua_State *L)
+{
+	struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+	struct stream *s = appctx->htxn.s;
+	struct hlua *hlua = &s->hlua;
+
+	MAY_LJMP(check_args(L, 2, "set_priv"));
+
+	/* Remove previous value. */
+	if (hlua->Mref != -1)
+		luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
+
+	/* Get and store new value. */
+	lua_pushvalue(L, 2); /* Copy the element 2 at the top of the stack. */
+	hlua->Mref = luaL_ref(L, LUA_REGISTRYINDEX); /* pop the previously pushed value. */
+
+	return 0;
+}
+
+__LJMP static int hlua_applet_http_get_priv(lua_State *L)
+{
+	struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+	struct stream *s = appctx->htxn.s;
+	struct hlua *hlua = &s->hlua;
+
+	/* Push configuration index in the stack. */
+	lua_rawgeti(L, LUA_REGISTRYINDEX, hlua->Mref);
+
+	return 1;
+}
+
+>>>>>>> c43224e... BUG/MEDIUM: lua: memory leak
 /* If expected data not yet available, it returns a yield. This function
  * consumes the data in the buffer. It returns a string containing the
  * data. This string can be empty.
@@ -4646,7 +4680,7 @@ __LJMP static int hlua_set_priv(lua_State *L)
 
 	/* Remove previous value. */
 	if (hlua->Mref != -1)
-		luaL_unref(L, hlua->Mref, LUA_REGISTRYINDEX);
+		luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
 
 	/* Get and store new value. */
 	lua_pushvalue(L, 2); /* Copy the element 2 at the top of the stack. */
-- 
1.7.10.4

>From 355d9493bcc738c7ae5fb0639511a47612416505 Mon Sep 17 00:00:00 2001
From: Thierry FOURNIER <[email protected]>
Date: Wed, 26 Apr 2017 14:25:58 +0200
Subject: [PATCH 1/2] BUG/MEDIUM: lua: memory leak

The priv context is not cleaned when we set a new priv context.
This is caused by a stupid swap between two parameter of the
luaL_unref() function.

workaround: use set_priv only once when we process a stream.

This patch should be backported in version 1.7 and 1.6
---
 src/hlua.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/hlua.c b/src/hlua.c
index 77e5a9d..6ce18e1 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -3310,7 +3310,7 @@ __LJMP static int hlua_applet_tcp_set_priv(lua_State *L)
 
 	/* Remove previous value. */
 	if (hlua->Mref != -1)
-		luaL_unref(L, hlua->Mref, LUA_REGISTRYINDEX);
+		luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
 
 	/* Get and store new value. */
 	lua_pushvalue(L, 2); /* Copy the element 2 at the top of the stack. */
@@ -3773,7 +3773,7 @@ __LJMP static int hlua_applet_http_set_priv(lua_State *L)
 
 	/* Remove previous value. */
 	if (hlua->Mref != -1)
-		luaL_unref(L, hlua->Mref, LUA_REGISTRYINDEX);
+		luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
 
 	/* Get and store new value. */
 	lua_pushvalue(L, 2); /* Copy the element 2 at the top of the stack. */
@@ -4860,7 +4860,7 @@ __LJMP static int hlua_set_priv(lua_State *L)
 
 	/* Remove previous value. */
 	if (hlua->Mref != -1)
-		luaL_unref(L, hlua->Mref, LUA_REGISTRYINDEX);
+		luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
 
 	/* Get and store new value. */
 	lua_pushvalue(L, 2); /* Copy the element 2 at the top of the stack. */
-- 
1.7.10.4

>From 4b9178074eef43a5d1f7ba984d34580a6ec5f3ce Mon Sep 17 00:00:00 2001
From: Thierry FOURNIER <[email protected]>
Date: Wed, 26 Apr 2017 13:27:05 +0200
Subject: [PATCH 2/2] CLEANUP: lua: remove test

The man of "luaL_unref" says "If ref is LUA_NOREF or LUA_REFNIL,
luaL_unref does nothing.", so I remove the check.
---
 src/hlua.c |    9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/src/hlua.c b/src/hlua.c
index 6ce18e1..643d3fc 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -3309,8 +3309,7 @@ __LJMP static int hlua_applet_tcp_set_priv(lua_State *L)
 	MAY_LJMP(check_args(L, 2, "set_priv"));
 
 	/* Remove previous value. */
-	if (hlua->Mref != -1)
-		luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
+	luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
 
 	/* Get and store new value. */
 	lua_pushvalue(L, 2); /* Copy the element 2 at the top of the stack. */
@@ -3772,8 +3771,7 @@ __LJMP static int hlua_applet_http_set_priv(lua_State *L)
 	MAY_LJMP(check_args(L, 2, "set_priv"));
 
 	/* Remove previous value. */
-	if (hlua->Mref != -1)
-		luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
+	luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
 
 	/* Get and store new value. */
 	lua_pushvalue(L, 2); /* Copy the element 2 at the top of the stack. */
@@ -4859,8 +4857,7 @@ __LJMP static int hlua_set_priv(lua_State *L)
 	hlua = hlua_gethlua(L);
 
 	/* Remove previous value. */
-	if (hlua->Mref != -1)
-		luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
+	luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);
 
 	/* Get and store new value. */
 	lua_pushvalue(L, 2); /* Copy the element 2 at the top of the stack. */
-- 
1.7.10.4

Reply via email to