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?

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 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`.

The script prints a line ending in numbers 1 through 50 every 2 seconds. I 
would expect haproxy to log the same output, but instead I get the following:
```
$ ./haproxy -f haproxy_yieldtest.conf
[NOTICE]   (123028) : haproxy version is 3.2-dev10-2c3d65-67
[NOTICE]   (123028) : path to executable is ./haproxy
[WARNING]  (123028) : config : hlua: please set 
"tune.lua.bool-sample-conversion" tunable to either "normal" or "pre-3.1-bug" 
explicitly to avoid ambiguities. Defaulting to "pre-3.1-bug".
Connect from 127.0.0.1:52014 to 127.0.0.1:9090 (echo/TCP)
Start printing input...
Received line: 1_foo_1.
Received line: 1_foo_18.
Received line: 1_foo_36.
Received line: 1_foo_39.
Received line: 1_foo_40.
Received line: 1_foo_49.
Received line: 1_foo_50.
Received line: 2_foo_1.
Received line: 2_foo_38.
Received line: 3_foo_1.
Received line: 4_foo_1.
Received line: 4_foo_35.
Received line: 4_foo_41.
Received line: 5_foo_1.
Received line: 6_foo_1.
```

Clearly it's missing a whole lot of input lines, is this a bug or am I just 
trying to use services/applets to achieve something they're not meant to 
achieve?
Any help would be much appreciated!

Thanks
Jacques

Reply via email to