Hi, On 4/17/25 13:05, Jacques Heunis (BLOOMBERG/ LONDON) wrote: > Hi there, > > I'm trying to setup a simple TCP service in lua (via > `core.register_service()`) to accept several lines of text at a time from an > external process and I've recently run into issues when that lua yields > (either forcibly or intentionally). > More specifically it appears that whenever the service function yields, the > data that has already been received but not yet processed is lost because the > channel's buffer gets cleared out. > I can reliably reproduce this on 2.8.0, 3.1.0 and master as of commit 2c3d656 > (latest at the time of writing), details below. > > I understand that some lua contexts are not allowed to yield but its not > clear if this is the case for applets/services. More broadly it's not clear > to me from the documentation what context such a service runs in. > The documentation for `core.register_service()` does not specify the context > in which the service function will be run and while the documentation does > mention an "applet" context, it appears only once under `core.thread`. > So my first question is: Is a service/applet function allowed to yield?
Yes indeed a service is allowed to yield and will yield automatically after tune.lua.forced-yield lua instructions which defaults to 10000 > > I assume so because `AppletTCP.getline()` itself yields internally if no data > is immediately available. > However, if they are allowed to yield then this seems like a bug since I > would certainly not have expected that my TCP-based service would observe > gaps in the data it receives. I suspect it is a bug as well, doesn't sound right > > I can reliably reproduce this behaviour using the following setup: > haproxy config: > ``` > global > log stdout format raw local0 info > lua-load haproxy_yieldtest.lua > > defaults > log global > timeout connect 10s > timeout client 1m > timeout server 1m > > listen echo > bind *:9090 > mode tcp > tcp-request content use-service lua.print_input > ``` > > haproxy_yieldtest.lua: > ``` > core.register_service("print_input", "tcp", function(applet) > core.Info("Start printing input...") > while true do > local inputs = applet:getline() > if inputs == nil or string.len(inputs) == 0 then > core.Info("closing input connection") > return > end > core.Info("Received line: "..inputs) > core.yield() > end > end) > ``` > > and then testing it by running this simple bash script: > ``` > #!/usr/bin/bash > for i in $(seq 1 9999); do > for j in $(seq 1 50); do > echo "${i}_foo_${j}" > done > sleep 2 > done > ``` > > and running it with `./test_seq.sh | netcat localhost 9090`. Thank you very much for this reproducer, it was very helpful. Turns out this used to work fine in older stable releases (ie: 2.4, 2.6) and somehow it stopped working properly near 2.8 indeed A quick bisect based on your reproducer tells us that: > commit 31572229ede61eb4ce1ee494f4436df353165a43 > Author: Christopher Faulet <cfau...@haproxy.com> > Date: Fri Mar 31 11:13:48 2023 +0200 > > MEDIUM: hlua/applet: Use the sedesc to report and detect end of processing > > There are 3 kinds of applet in lua: The co-sockets, the TCP services and > the > HTTP services. The three are refactored to use the SE descriptor instead > of > the channel to report error and end-of-stream. > > src/hlua.c | 108 > +++++++++++++++++++++++++++---------------------------------- > 1 file changed, 48 insertions(+), 60 deletions(-) Is the first commit that exhibits this unexpected behavior/bug. So indeed all versions since 2.8 appear to be affected. We'll try to understand the regression and provide a fix shortly, thank you very much! Regards, Aurelien