Re: nbproc 1 vs >1 performance

2016-04-15 Thread Pavlos Parissis


On 15/04/2016 11:44 πμ, Willy Tarreau wrote:
> Hi Christian,
> 
> On Fri, Apr 15, 2016 at 11:26:18AM +0200, Christian Ruppert wrote:
>> Just in case someone is interested in this setup:
>> Don't put the two SSL binds into the frontend. Add a second listener for the
>> two SSL binds and from there via send-proxy-v2 to the frontend.
>> Why? Because having >1 processes in the frontend in combination with a
>> backend with active checks is a bad idea. If you have for example 10
>> loadbalancer with 40 process each you'll basically DDoS yourself.
> 
> Definitely. That's why the recommended setup is indeed to have two layers,
> one for SSL and one for L7, possibly connected over unix or abstract sockets :
> 
>   listen ssl-offload
>   bind-process 2-40
>   bind :443 ssl crt foo ... process 2
>   ...
>   bind :443 ssl crt foo ... process 40
>   server clear abns@clear send-proxy-v2
> 
>   frontend clear
>   bind-process 1
>   bind :80 ... # clear traffic
>   bind abns@clear accept-proxy
>   ...
>   default_backend application...
> 
>   etc.
> 

But in case of HTTPs on the backend side, you can't use the above as you
need CPU power on the backend.


Cheers,
Pavlos



signature.asc
Description: OpenPGP digital signature


Re: nbproc 1 vs >1 performance

2016-04-15 Thread Willy Tarreau
On Fri, Apr 15, 2016 at 11:56:20AM +0200, Pavlos Parissis wrote:
> nbproc > 1 introduces also 2 other __issues__ which has been discussed
> several times in this ML:
> 
> 1) the aggregation of statistics
> 2) performing operations such as disable/enable/change weight.
> In setups where the frontends/backends is unevenly distributed across
> all process, this becomes more apparent as you have to track which
> process manages which frontend.

All of them are properly solved by having the backend in a single process.
Even peers work fine this way :-) You just need to consider the front SSL
processes as dumb off-loaders.

Cheers,
Willy




Re: nbproc 1 vs >1 performance

2016-04-15 Thread Pavlos Parissis


On 15/04/2016 11:26 πμ, Christian Ruppert wrote:
> On 2016-04-14 11:06, Christian Ruppert wrote:
>> Hi Willy,
>>
>> On 2016-04-14 10:17, Willy Tarreau wrote:
>>> On Thu, Apr 14, 2016 at 08:55:47AM +0200, Lukas Tribus wrote:
 Le me put it this way:

 frontend haproxy_test
  bind-process 1-8
  bind :12345 process 1
  bind :12345 process 2
  bind :12345 process 3
  bind :12345 process 4


 Leads to 8 processes, and the master process binds the socket 4
 times (PID
 16509):

>>> (...)
 lukas@ubuntuvm:~/haproxy-1.5$ sudo netstat -tlp | grep hap
 tcp0  0 *:12345 *:* LISTEN 
 16509/haproxy
 tcp0  0 *:12345 *:* LISTEN 
 16509/haproxy
 tcp0  0 *:12345 *:* LISTEN 
 16509/haproxy
 tcp0  0 *:12345 *:* LISTEN 
 16509/haproxy
 lukas@ubuntuvm:~/haproxy-1.5$
>>>
>>> OK so it's netstat which gives a wrong report, I have the same here.
>>> I verified
>>> in /proc/$PID/fd/ and I properly saw the FDs. Next, "ss -anp" also
>>> shows all the
>>> process list :
>>>
>>>   LISTEN 0  128   *:12345
>>>   *:*
>>> users:(("haproxy",25360,7),("haproxy",25359,7),("haproxy",25358,7),("haproxy",25357,7),("haproxy",25356,7),("haproxy",25355,7),("haproxy",25354,7),("haproxy",25353,7))
>>>
>>>   LISTEN 0  128   *:12345
>>>   *:*
>>> users:(("haproxy",25360,6),("haproxy",25359,6),("haproxy",25358,6),("haproxy",25357,6),("haproxy",25356,6),("haproxy",25355,6),("haproxy",25354,6),("haproxy",25353,6))
>>>
>>>   LISTEN 0  128   *:12345
>>>   *:*
>>> users:(("haproxy",25360,5),("haproxy",25359,5),("haproxy",25358,5),("haproxy",25357,5),("haproxy",25356,5),("haproxy",25355,5),("haproxy",25354,5),("haproxy",25353,5))
>>>
>>>   LISTEN 0  128   *:12345
>>>   *:*
>>> users:(("haproxy",25360,4),("haproxy",25359,4),("haproxy",25358,4),("haproxy",25357,4),("haproxy",25356,4),("haproxy",25355,4),("haproxy",25354,4),("haproxy",25353,4))
>>>
>>>
>>> A performance test also shows a fair distribution of the load :
>>>
>>>   25353 willy 20   0 21872 4216 1668 S   26  0.1   0:04.54 haproxy
>>>   25374 willy 20   0  7456  1080 S   25  0.0   0:02.26
>>> injectl464
>>>   25376 willy 20   0  7456  1080 S   25  0.0   0:02.27
>>> injectl464
>>>   25377 willy 20   0  7456  1080 S   25  0.0   0:02.26
>>> injectl464
>>>   25375 willy 20   0  7456  1080 S   24  0.0   0:02.26
>>> injectl464
>>>   25354 willy 20   0 21872 4168 1620 R   22  0.1   0:04.51 haproxy
>>>   25356 willy 20   0 21872 4216 1668 R   22  0.1   0:04.21 haproxy
>>>   25355 willy 20   0 21872 4168 1620 S   21  0.1   0:04.38 haproxy
>>>
>>> However, as you can see these sockets are still bound to all
>>> processes and
>>> that's not a good idea in the multi-queue mode.
>>>
>>> I have added a few debug lines in enable_listener() like this :
>>>
>>> $ git diff
>>> diff --git a/src/listener.c b/src/listener.c
>>> index 5abeb80..59c51a1 100644
>>> --- a/src/listener.c
>>> +++ b/src/listener.c
>>> @@ -49,6 +49,7 @@ static struct bind_kw_list bind_keywords = {
>>>   */
>>>  void enable_listener(struct listener *listener)
>>>  {
>>> +   fddebug("%d: enabling fd %d\n", getpid(), listener->fd);
>>> if (listener->state == LI_LISTEN) {
>>> if ((global.mode & (MODE_DAEMON | MODE_SYSTEMD)) &&
>>> listener->bind_conf->bind_proc &&
>>> @@ -57,6 +58,7 @@ void enable_listener(struct listener *listener)
>>>  * want any fd event to reach it.
>>>  */
>>> fd_stop_recv(listener->fd);
>>> +   fddebug("%d: pausing fd %d\n", getpid(),
>>> listener->fd);
>>> listener->state = LI_PAUSED;
>>> }
>>> else if (listener->nbconn < listener->maxconn) {
>>>
>>> And we're seeing this upon startup for processes 25746..25755 :
>>>
>>> Thus as you can see that FDs are properly enabled and paused for the
>>> unavailable ones.
>>>
>>> willy@wtap:haproxy$ grep 4294967295 log | grep 25746
>>> 25746 write(4294967295, "25746: enabling fd 4\n", 21 
>>> 25746 write(4294967295, "25746: enabling fd 5\n", 21 
>>> 25746 write(4294967295, "25746: pausing fd 5\n", 20) = -1 EBADF (Bad
>>> file descriptor)
>>> 25746 write(4294967295, "25746: enabling fd 6\n", 21) = -1 EBADF (Bad
>>> file descriptor)
>>> 25746 write(4294967295, "25746: pausing fd 6\n", 20) = -1 EBADF (Bad
>>> file descriptor)
>>> 25746 write(4294967295, "25746: enabling fd 7\n", 21 
>>> 25746 write(4294967295, "25746: pausing fd 7\n", 20 
>>> willy@wtap:haproxy$ grep 4294967295 log | grep 25747
>>> 25747 write(4294967295, "25747: enabling fd 4\n", 21 
>>> 25747 write(4294967295, "25747: pausing fd 4\n", 20 
>>> 25747 write(

Re: nbproc 1 vs >1 performance

2016-04-15 Thread Willy Tarreau
Hi Christian,

On Fri, Apr 15, 2016 at 11:26:18AM +0200, Christian Ruppert wrote:
> Just in case someone is interested in this setup:
> Don't put the two SSL binds into the frontend. Add a second listener for the
> two SSL binds and from there via send-proxy-v2 to the frontend.
> Why? Because having >1 processes in the frontend in combination with a
> backend with active checks is a bad idea. If you have for example 10
> loadbalancer with 40 process each you'll basically DDoS yourself.

Definitely. That's why the recommended setup is indeed to have two layers,
one for SSL and one for L7, possibly connected over unix or abstract sockets :

  listen ssl-offload
  bind-process 2-40
  bind :443 ssl crt foo ... process 2
  ...
  bind :443 ssl crt foo ... process 40
  server clear abns@clear send-proxy-v2

  frontend clear
  bind-process 1
  bind :80 ... # clear traffic
  bind abns@clear accept-proxy
  ...
  default_backend application...

  etc.

> We'll have
> to wait for multi-threaded for the setup above or some kind of dedicated
> check process that shares the information with all other processes.

No, you really don't want to multiply your backends, it causes pain
for checks, for monitoring, for maxconn etc. Prefer to have a single
instance if that sustains the load.

Willy



Re: nbproc 1 vs >1 performance

2016-04-15 Thread Christian Ruppert

On 2016-04-14 11:06, Christian Ruppert wrote:

Hi Willy,

On 2016-04-14 10:17, Willy Tarreau wrote:

On Thu, Apr 14, 2016 at 08:55:47AM +0200, Lukas Tribus wrote:

Le me put it this way:

frontend haproxy_test
 bind-process 1-8
 bind :12345 process 1
 bind :12345 process 2
 bind :12345 process 3
 bind :12345 process 4


Leads to 8 processes, and the master process binds the socket 4 times 
(PID

16509):


(...)

lukas@ubuntuvm:~/haproxy-1.5$ sudo netstat -tlp | grep hap
tcp0  0 *:12345 *:* LISTEN  
16509/haproxy
tcp0  0 *:12345 *:* LISTEN  
16509/haproxy
tcp0  0 *:12345 *:* LISTEN  
16509/haproxy
tcp0  0 *:12345 *:* LISTEN  
16509/haproxy

lukas@ubuntuvm:~/haproxy-1.5$


OK so it's netstat which gives a wrong report, I have the same here. I 
verified
in /proc/$PID/fd/ and I properly saw the FDs. Next, "ss -anp" also 
shows all the

process list :

  LISTEN 0  128   *:12345
  *:*
users:(("haproxy",25360,7),("haproxy",25359,7),("haproxy",25358,7),("haproxy",25357,7),("haproxy",25356,7),("haproxy",25355,7),("haproxy",25354,7),("haproxy",25353,7))
  LISTEN 0  128   *:12345
  *:*
users:(("haproxy",25360,6),("haproxy",25359,6),("haproxy",25358,6),("haproxy",25357,6),("haproxy",25356,6),("haproxy",25355,6),("haproxy",25354,6),("haproxy",25353,6))
  LISTEN 0  128   *:12345
  *:*
users:(("haproxy",25360,5),("haproxy",25359,5),("haproxy",25358,5),("haproxy",25357,5),("haproxy",25356,5),("haproxy",25355,5),("haproxy",25354,5),("haproxy",25353,5))
  LISTEN 0  128   *:12345
  *:*
users:(("haproxy",25360,4),("haproxy",25359,4),("haproxy",25358,4),("haproxy",25357,4),("haproxy",25356,4),("haproxy",25355,4),("haproxy",25354,4),("haproxy",25353,4))

A performance test also shows a fair distribution of the load :

  25353 willy 20   0 21872 4216 1668 S   26  0.1   0:04.54 haproxy
  25374 willy 20   0  7456  1080 S   25  0.0   0:02.26 
injectl464
  25376 willy 20   0  7456  1080 S   25  0.0   0:02.27 
injectl464
  25377 willy 20   0  7456  1080 S   25  0.0   0:02.26 
injectl464
  25375 willy 20   0  7456  1080 S   24  0.0   0:02.26 
injectl464

  25354 willy 20   0 21872 4168 1620 R   22  0.1   0:04.51 haproxy
  25356 willy 20   0 21872 4216 1668 R   22  0.1   0:04.21 haproxy
  25355 willy 20   0 21872 4168 1620 S   21  0.1   0:04.38 haproxy

However, as you can see these sockets are still bound to all processes 
and

that's not a good idea in the multi-queue mode.

I have added a few debug lines in enable_listener() like this :

$ git diff
diff --git a/src/listener.c b/src/listener.c
index 5abeb80..59c51a1 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -49,6 +49,7 @@ static struct bind_kw_list bind_keywords = {
  */
 void enable_listener(struct listener *listener)
 {
+   fddebug("%d: enabling fd %d\n", getpid(), listener->fd);
if (listener->state == LI_LISTEN) {
if ((global.mode & (MODE_DAEMON | MODE_SYSTEMD)) &&
listener->bind_conf->bind_proc &&
@@ -57,6 +58,7 @@ void enable_listener(struct listener *listener)
 * want any fd event to reach it.
 */
fd_stop_recv(listener->fd);
+   fddebug("%d: pausing fd %d\n", getpid(), 
listener->fd);

listener->state = LI_PAUSED;
}
else if (listener->nbconn < listener->maxconn) {

And we're seeing this upon startup for processes 25746..25755 :

Thus as you can see that FDs are properly enabled and paused for the
unavailable ones.

willy@wtap:haproxy$ grep 4294967295 log | grep 25746
25746 write(4294967295, "25746: enabling fd 4\n", 21 
25746 write(4294967295, "25746: enabling fd 5\n", 21 
25746 write(4294967295, "25746: pausing fd 5\n", 20) = -1 EBADF (Bad
file descriptor)
25746 write(4294967295, "25746: enabling fd 6\n", 21) = -1 EBADF (Bad
file descriptor)
25746 write(4294967295, "25746: pausing fd 6\n", 20) = -1 EBADF (Bad
file descriptor)
25746 write(4294967295, "25746: enabling fd 7\n", 21 
25746 write(4294967295, "25746: pausing fd 7\n", 20 
willy@wtap:haproxy$ grep 4294967295 log | grep 25747
25747 write(4294967295, "25747: enabling fd 4\n", 21 
25747 write(4294967295, "25747: pausing fd 4\n", 20 
25747 write(4294967295, "25747: enabling fd 5\n", 21 
25747 write(4294967295, "25747: enabling fd 6\n", 21 
25747 write(4294967295, "25747: pausing fd 6\n", 20 
25747 write(4294967295, "25747: enabling fd 7\n", 21 
25747 write(4294967295, "25747: pausing fd 7\n", 20 
willy@wtap:haproxy$ grep 4294967295 log | grep 25748
25748 write(4294967295, "25748: enabling fd 4\n", 21 
25748 write(4294967295, "25748: pausing fd 4\n", 20 
25748 write(4294967295, "25748: enabling fd 5\n", 21 
25748 write(4294967295, "25748:

Re: nbproc 1 vs >1 performance

2016-04-14 Thread Willy Tarreau
On Thu, Apr 14, 2016 at 01:54:27PM +0200, Daniel Schneller wrote:
> Trying not to hijack the thread here, but it seems to fit well in the context:
> 
> Does this mean that in the following could happen due to the difference in 
> BSD/Linux SO_REUSEPORT:
> 
> 1. haproxy process ???A??? binds say port 1234
> 2. client A connects to 1234 and keeps the connection open
> 3. /etc/init.d/haproxy restart
> 4. haproxy process ???B??? starts and _also_ binds 1234
> 5. haproxy ???A??? is still around, due to client A
> 6. client B connects to 1234
> 
> Am I right to assume that client B can be handled by _either_ haproxy ???A???
> or ???B??? depending on the hash result underneath SO_REUSEPORT???s
> implementation? If so, that would explain some issues I had in the past when
> quickly iterating config changes and restarting haproxy each time, but
> sometimes getting results that could only have come from an older config?

Not exactly. "haproxy restart" is "haproxy stop ; haproxy start" so it will
stop the old process. However "haproxy reload" will do that yes. But the
old process (process A) releases the port on reload, so new connections
will only go to the new process. There's a tiny fraction of second during
which both processes are bound to the same socket which ensures there's no
connection lost, but it lasts one millisecond or so.

Willy




Re: nbproc 1 vs >1 performance

2016-04-14 Thread Daniel Schneller
Trying not to hijack the thread here, but it seems to fit well in the context:

Does this mean that in the following could happen due to the difference in 
BSD/Linux SO_REUSEPORT:

1. haproxy process “A” binds say port 1234
2. client A connects to 1234 and keeps the connection open
3. /etc/init.d/haproxy restart
4. haproxy process “B” starts and _also_ binds 1234
5. haproxy “A” is still around, due to client A
6. client B connects to 1234

Am I right to assume that client B can be handled by _either_ haproxy “A” or 
“B” depending on the hash result underneath SO_REUSEPORT’s implementation? If 
so, that would explain some issues I had in the past when quickly iterating 
config changes and restarting haproxy each time, but sometimes getting results 
that could only have come from an older config?

Thanks,
Daniel


-- 
Daniel Schneller
Principal Cloud Engineer
CenterDevice GmbH


> On 14.04.2016, at 12:01, Willy Tarreau  wrote:
> 
> On Thu, Apr 14, 2016 at 10:17:10AM +0200, Willy Tarreau wrote:
>> So I guess that indeed, if not all the processes a frontend is bound to
>> have a corresponding bind line, this can cause connection issues as some
>> incoming connections will be distributed to queues that nobody listens to.
> 
> I said rubish here. It's the same socket that is shared between all these
> processes, so there's no issue with all of them picking from the same queue
> even if in the end only one picks it. However I want to fix it to make things
> cleaner and easier to debug and observe (and not fool netstat as the previous
> example showed).
> 
> Willy
> 



Re: nbproc 1 vs >1 performance

2016-04-14 Thread Willy Tarreau
On Thu, Apr 14, 2016 at 10:17:10AM +0200, Willy Tarreau wrote:
> So I guess that indeed, if not all the processes a frontend is bound to
> have a corresponding bind line, this can cause connection issues as some
> incoming connections will be distributed to queues that nobody listens to.

I said rubish here. It's the same socket that is shared between all these
processes, so there's no issue with all of them picking from the same queue
even if in the end only one picks it. However I want to fix it to make things
cleaner and easier to debug and observe (and not fool netstat as the previous
example showed).

Willy



Re: nbproc 1 vs >1 performance

2016-04-14 Thread Willy Tarreau
On Thu, Apr 14, 2016 at 11:49:47AM +0200, Christian Ruppert wrote:
> Yep, that did it. With this setting there is no more performance decrease on
> the http bind. Thanks!
> I'm just not sure if that will (negatively) affect anything else.

It may, depending on your setup (eg: if some frontends can drain many
connections at once it can increase latency).

Better apply the attached patch instead which divides by the number of
processes the listener is bound to :-)

Willy

>From 7c0ffd23d2d13e464a401292dcf55f658f2d3e24 Mon Sep 17 00:00:00 2001
From: Willy Tarreau 
Date: Thu, 14 Apr 2016 11:47:38 +0200
Subject: BUG/MEDIUM: fix maxaccept computation on per-process listeners

Christian Ruppert reported a performance degradation when binding a
single frontend to many processes while only one bind line was being
used, bound to a single process.

The reason comes from the fact that whenever a listener is bound to
multiple processes, the it is assigned a maxaccept value which equals
half the global maxaccept value divided by the number of processes the
frontend is bound to. The purpose is to ensure that no single process
will drain all the incoming requests at once and ensure a fair share
between all listeners. Usually this works pretty well, when a listener
is bound to all the processes of its frontend. But here we're in a
situation where the maxaccept of a listener which is bound to a single
process is still divided by a large value.

The fix consists in taking into account the number of processes the
listener is bound do and not only those of the frontend. This way it
is perfectly possible to benefit from nbproc and SO_REUSEPORT without
performance degradation.

1.6 and 1.5 normally suffer from the same issue.
---
 src/cfgparse.c | 14 ++
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/cfgparse.c b/src/cfgparse.c
index 2d0a020..c3b29d4 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -8694,9 +8694,6 @@ out_uri_auth_compat:
for (curproxy = proxy; curproxy; curproxy = curproxy->next) {
struct listener *listener;
unsigned int next_id;
-   int nbproc;
-
-   nbproc = my_popcountl(curproxy->bind_proc & 
nbits(global.nbproc));
 
 #ifdef USE_OPENSSL
/* Configure SSL for each bind line.
@@ -8741,6 +8738,15 @@ out_uri_auth_compat:
/* adjust this proxy's listeners */
next_id = 1;
list_for_each_entry(listener, &curproxy->conf.listeners, by_fe) 
{
+   int nbproc;
+
+   nbproc = my_popcountl(curproxy->bind_proc &
+ listener->bind_conf->bind_proc &
+ nbits(global.nbproc));
+
+   if (!nbproc) /* no intersection between listener and 
frontend */
+   nbproc = 1;
+
if (!listener->luid) {
/* listener ID not set, use automatic numbering 
with first
 * spare entry starting with next_luid.
@@ -8819,7 +8825,7 @@ out_uri_auth_compat:
 #endif /* USE_OPENSSL */
}
 
-   if (nbproc > 1) {
+   if (my_popcountl(curproxy->bind_proc & nbits(global.nbproc)) > 
1) {
if (curproxy->uri_auth) {
int count, maxproc = 0;
 
-- 
1.7.12.1



Re: nbproc 1 vs >1 performance

2016-04-14 Thread Christian Ruppert

On 2016-04-14 11:41, Willy Tarreau wrote:

Hi Christian,

On Thu, Apr 14, 2016 at 11:06:02AM +0200, Christian Ruppert wrote:
I've applied your patch and I just looked at the performance so far. 
The

performance is still the same, so the lessperformant one is still less
performant than the moreperformant.cfg. So from the performance point 
of

view there's no difference between with and without that patch.

We have a (IMHO) quite huge haproxy config with around 200 frontends, 
~180
backends and ~90 listener. So the intention was to combine a http bind 
with
a ssl bind in one frontend so that we keep it at a minimum that is 
necessary

to get it working properly:
listen ssl-relay
mode tcp

bind-process 2

bind :443 process 2

tcp-request inspect-delay 7s
acl HAS_ECC req.ssl_ec_ext eq 1
tcp-request content accept if { req_ssl_hello_type 1 } # Client 
Hello


use-server ecc if HAS_ECC
server ecc unix@/var/run/haproxy_ssl_ecc.sock send-proxy-v2

use-server rsa if !HAS_ECC
server rsa unix@/var/run/haproxy_ssl_rsa.sock send-proxy-v2


frontend http_https_combined
mode http

bind-process 1-40

bind :80 process 1
bind unix@/var/run/haproxy_ssl_ecc.sock accept-proxy ssl crt
/etc/haproxy/ssltest.pem-ECC user haproxy process 4-40
bind unix@/var/run/haproxy_ssl_rsa.sock accept-proxy ssl crt
/etc/haproxy/ssltest.pem-RSA user haproxy process 3

...

default_backend somebackend


OK I know why. It's because there's a per-listener "maxaccept" setting
which divides the global.tune.maxaccept value by 2 then by the number
of processes a listener is bound to. But in fact it divides by the 
number
of processes the frontend is bound to. In the past this used to be 
efficient
but since the "process" directive on frontends it doesn't make sense 
anymore.
So in practice above you end up with "maxaccept 1". You could try to 
work
around it by setting "tune.maxaccept 4000" in your global section, 
you'll

get the original performance back.


Yep, that did it. With this setting there is no more performance 
decrease on the http bind. Thanks!

I'm just not sure if that will (negatively) affect anything else.



I have to change this obviously, so that we consider the intersection
between the listener and the frontend and not the frontend only. I'm
looking at this right now.

Willy


--
Regards,
Christian Ruppert



Re: nbproc 1 vs >1 performance

2016-04-14 Thread Willy Tarreau
Hi Christian,

On Thu, Apr 14, 2016 at 11:06:02AM +0200, Christian Ruppert wrote:
> I've applied your patch and I just looked at the performance so far. The
> performance is still the same, so the lessperformant one is still less
> performant than the moreperformant.cfg. So from the performance point of
> view there's no difference between with and without that patch.
> 
> We have a (IMHO) quite huge haproxy config with around 200 frontends, ~180
> backends and ~90 listener. So the intention was to combine a http bind with
> a ssl bind in one frontend so that we keep it at a minimum that is necessary
> to get it working properly:
> listen ssl-relay
> mode tcp
> 
> bind-process 2
> 
> bind :443 process 2
> 
> tcp-request inspect-delay 7s
> acl HAS_ECC req.ssl_ec_ext eq 1
> tcp-request content accept if { req_ssl_hello_type 1 } # Client Hello
> 
> use-server ecc if HAS_ECC
> server ecc unix@/var/run/haproxy_ssl_ecc.sock send-proxy-v2
> 
> use-server rsa if !HAS_ECC
> server rsa unix@/var/run/haproxy_ssl_rsa.sock send-proxy-v2
> 
> 
> frontend http_https_combined
> mode http
> 
> bind-process 1-40
> 
> bind :80 process 1
> bind unix@/var/run/haproxy_ssl_ecc.sock accept-proxy ssl crt
> /etc/haproxy/ssltest.pem-ECC user haproxy process 4-40
> bind unix@/var/run/haproxy_ssl_rsa.sock accept-proxy ssl crt
> /etc/haproxy/ssltest.pem-RSA user haproxy process 3
> 
> ...
> 
> default_backend somebackend

OK I know why. It's because there's a per-listener "maxaccept" setting
which divides the global.tune.maxaccept value by 2 then by the number
of processes a listener is bound to. But in fact it divides by the number
of processes the frontend is bound to. In the past this used to be efficient
but since the "process" directive on frontends it doesn't make sense anymore.
So in practice above you end up with "maxaccept 1". You could try to work
around it by setting "tune.maxaccept 4000" in your global section, you'll
get the original performance back.

I have to change this obviously, so that we consider the intersection
between the listener and the frontend and not the frontend only. I'm
looking at this right now.

Willy




Re: nbproc 1 vs >1 performance

2016-04-14 Thread Christian Ruppert

Hi Willy,

On 2016-04-14 10:17, Willy Tarreau wrote:

On Thu, Apr 14, 2016 at 08:55:47AM +0200, Lukas Tribus wrote:

Le me put it this way:

frontend haproxy_test
 bind-process 1-8
 bind :12345 process 1
 bind :12345 process 2
 bind :12345 process 3
 bind :12345 process 4


Leads to 8 processes, and the master process binds the socket 4 times 
(PID

16509):


(...)

lukas@ubuntuvm:~/haproxy-1.5$ sudo netstat -tlp | grep hap
tcp0  0 *:12345 *:* LISTEN  
16509/haproxy
tcp0  0 *:12345 *:* LISTEN  
16509/haproxy
tcp0  0 *:12345 *:* LISTEN  
16509/haproxy
tcp0  0 *:12345 *:* LISTEN  
16509/haproxy

lukas@ubuntuvm:~/haproxy-1.5$


OK so it's netstat which gives a wrong report, I have the same here. I 
verified
in /proc/$PID/fd/ and I properly saw the FDs. Next, "ss -anp" also 
shows all the

process list :

  LISTEN 0  128   *:12345
  *:*
users:(("haproxy",25360,7),("haproxy",25359,7),("haproxy",25358,7),("haproxy",25357,7),("haproxy",25356,7),("haproxy",25355,7),("haproxy",25354,7),("haproxy",25353,7))
  LISTEN 0  128   *:12345
  *:*
users:(("haproxy",25360,6),("haproxy",25359,6),("haproxy",25358,6),("haproxy",25357,6),("haproxy",25356,6),("haproxy",25355,6),("haproxy",25354,6),("haproxy",25353,6))
  LISTEN 0  128   *:12345
  *:*
users:(("haproxy",25360,5),("haproxy",25359,5),("haproxy",25358,5),("haproxy",25357,5),("haproxy",25356,5),("haproxy",25355,5),("haproxy",25354,5),("haproxy",25353,5))
  LISTEN 0  128   *:12345
  *:*
users:(("haproxy",25360,4),("haproxy",25359,4),("haproxy",25358,4),("haproxy",25357,4),("haproxy",25356,4),("haproxy",25355,4),("haproxy",25354,4),("haproxy",25353,4))

A performance test also shows a fair distribution of the load :

  25353 willy 20   0 21872 4216 1668 S   26  0.1   0:04.54 haproxy
  25374 willy 20   0  7456  1080 S   25  0.0   0:02.26 
injectl464
  25376 willy 20   0  7456  1080 S   25  0.0   0:02.27 
injectl464
  25377 willy 20   0  7456  1080 S   25  0.0   0:02.26 
injectl464
  25375 willy 20   0  7456  1080 S   24  0.0   0:02.26 
injectl464

  25354 willy 20   0 21872 4168 1620 R   22  0.1   0:04.51 haproxy
  25356 willy 20   0 21872 4216 1668 R   22  0.1   0:04.21 haproxy
  25355 willy 20   0 21872 4168 1620 S   21  0.1   0:04.38 haproxy

However, as you can see these sockets are still bound to all processes 
and

that's not a good idea in the multi-queue mode.

I have added a few debug lines in enable_listener() like this :

$ git diff
diff --git a/src/listener.c b/src/listener.c
index 5abeb80..59c51a1 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -49,6 +49,7 @@ static struct bind_kw_list bind_keywords = {
  */
 void enable_listener(struct listener *listener)
 {
+   fddebug("%d: enabling fd %d\n", getpid(), listener->fd);
if (listener->state == LI_LISTEN) {
if ((global.mode & (MODE_DAEMON | MODE_SYSTEMD)) &&
listener->bind_conf->bind_proc &&
@@ -57,6 +58,7 @@ void enable_listener(struct listener *listener)
 * want any fd event to reach it.
 */
fd_stop_recv(listener->fd);
+   fddebug("%d: pausing fd %d\n", getpid(), 
listener->fd);

listener->state = LI_PAUSED;
}
else if (listener->nbconn < listener->maxconn) {

And we're seeing this upon startup for processes 25746..25755 :

Thus as you can see that FDs are properly enabled and paused for the
unavailable ones.

willy@wtap:haproxy$ grep 4294967295 log | grep 25746
25746 write(4294967295, "25746: enabling fd 4\n", 21 
25746 write(4294967295, "25746: enabling fd 5\n", 21 
25746 write(4294967295, "25746: pausing fd 5\n", 20) = -1 EBADF (Bad
file descriptor)
25746 write(4294967295, "25746: enabling fd 6\n", 21) = -1 EBADF (Bad
file descriptor)
25746 write(4294967295, "25746: pausing fd 6\n", 20) = -1 EBADF (Bad
file descriptor)
25746 write(4294967295, "25746: enabling fd 7\n", 21 
25746 write(4294967295, "25746: pausing fd 7\n", 20 
willy@wtap:haproxy$ grep 4294967295 log | grep 25747
25747 write(4294967295, "25747: enabling fd 4\n", 21 
25747 write(4294967295, "25747: pausing fd 4\n", 20 
25747 write(4294967295, "25747: enabling fd 5\n", 21 
25747 write(4294967295, "25747: enabling fd 6\n", 21 
25747 write(4294967295, "25747: pausing fd 6\n", 20 
25747 write(4294967295, "25747: enabling fd 7\n", 21 
25747 write(4294967295, "25747: pausing fd 7\n", 20 
willy@wtap:haproxy$ grep 4294967295 log | grep 25748
25748 write(4294967295, "25748: enabling fd 4\n", 21 
25748 write(4294967295, "25748: pausing fd 4\n", 20 
25748 write(4294967295, "25748: enabling fd 5\n", 21 
25748 write(4294967295, "25748: pausing fd 5\n", 20 
25748 write(4294967295, "

Re: nbproc 1 vs >1 performance

2016-04-14 Thread Willy Tarreau
On Thu, Apr 14, 2016 at 08:55:47AM +0200, Lukas Tribus wrote:
> Le me put it this way:
>
> frontend haproxy_test
>  bind-process 1-8
>  bind :12345 process 1
>  bind :12345 process 2
>  bind :12345 process 3
>  bind :12345 process 4
>
>
> Leads to 8 processes, and the master process binds the socket 4 times (PID
> 16509):
>
(...)
> lukas@ubuntuvm:~/haproxy-1.5$ sudo netstat -tlp | grep hap
> tcp0  0 *:12345 *:* LISTEN  16509/haproxy
> tcp0  0 *:12345 *:* LISTEN  16509/haproxy
> tcp0  0 *:12345 *:* LISTEN  16509/haproxy
> tcp0  0 *:12345 *:* LISTEN  16509/haproxy
> lukas@ubuntuvm:~/haproxy-1.5$

OK so it's netstat which gives a wrong report, I have the same here. I verified
in /proc/$PID/fd/ and I properly saw the FDs. Next, "ss -anp" also shows all the
process list :

  LISTEN 0  128   *:12345*:*
  
users:(("haproxy",25360,7),("haproxy",25359,7),("haproxy",25358,7),("haproxy",25357,7),("haproxy",25356,7),("haproxy",25355,7),("haproxy",25354,7),("haproxy",25353,7))
  LISTEN 0  128   *:12345*:*
  
users:(("haproxy",25360,6),("haproxy",25359,6),("haproxy",25358,6),("haproxy",25357,6),("haproxy",25356,6),("haproxy",25355,6),("haproxy",25354,6),("haproxy",25353,6))
  LISTEN 0  128   *:12345*:*
  
users:(("haproxy",25360,5),("haproxy",25359,5),("haproxy",25358,5),("haproxy",25357,5),("haproxy",25356,5),("haproxy",25355,5),("haproxy",25354,5),("haproxy",25353,5))
  LISTEN 0  128   *:12345*:*
  
users:(("haproxy",25360,4),("haproxy",25359,4),("haproxy",25358,4),("haproxy",25357,4),("haproxy",25356,4),("haproxy",25355,4),("haproxy",25354,4),("haproxy",25353,4))

A performance test also shows a fair distribution of the load :

  25353 willy 20   0 21872 4216 1668 S   26  0.1   0:04.54 haproxy
  25374 willy 20   0  7456  1080 S   25  0.0   0:02.26 injectl464
  25376 willy 20   0  7456  1080 S   25  0.0   0:02.27 injectl464
  25377 willy 20   0  7456  1080 S   25  0.0   0:02.26 injectl464
  25375 willy 20   0  7456  1080 S   24  0.0   0:02.26 injectl464
  25354 willy 20   0 21872 4168 1620 R   22  0.1   0:04.51 haproxy
  25356 willy 20   0 21872 4216 1668 R   22  0.1   0:04.21 haproxy
  25355 willy 20   0 21872 4168 1620 S   21  0.1   0:04.38 haproxy

However, as you can see these sockets are still bound to all processes and
that's not a good idea in the multi-queue mode.

I have added a few debug lines in enable_listener() like this :

$ git diff
diff --git a/src/listener.c b/src/listener.c
index 5abeb80..59c51a1 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -49,6 +49,7 @@ static struct bind_kw_list bind_keywords = {
  */
 void enable_listener(struct listener *listener)
 {
+   fddebug("%d: enabling fd %d\n", getpid(), listener->fd);
if (listener->state == LI_LISTEN) {
if ((global.mode & (MODE_DAEMON | MODE_SYSTEMD)) &&
listener->bind_conf->bind_proc &&
@@ -57,6 +58,7 @@ void enable_listener(struct listener *listener)
 * want any fd event to reach it.
 */
fd_stop_recv(listener->fd);
+   fddebug("%d: pausing fd %d\n", getpid(), listener->fd);
listener->state = LI_PAUSED;
}
else if (listener->nbconn < listener->maxconn) {

And we're seeing this upon startup for processes 25746..25755 :

Thus as you can see that FDs are properly enabled and paused for the
unavailable ones.

willy@wtap:haproxy$ grep 4294967295 log | grep 25746
25746 write(4294967295, "25746: enabling fd 4\n", 21 
25746 write(4294967295, "25746: enabling fd 5\n", 21 
25746 write(4294967295, "25746: pausing fd 5\n", 20) = -1 EBADF (Bad file 
descriptor)
25746 write(4294967295, "25746: enabling fd 6\n", 21) = -1 EBADF (Bad file 
descriptor)
25746 write(4294967295, "25746: pausing fd 6\n", 20) = -1 EBADF (Bad file 
descriptor)
25746 write(4294967295, "25746: enabling fd 7\n", 21 
25746 write(4294967295, "25746: pausing fd 7\n", 20 
willy@wtap:haproxy$ grep 4294967295 log | grep 25747
25747 write(4294967295, "25747: enabling fd 4\n", 21 
25747 write(4294967295, "25747: pausing fd 4\n", 20 
25747 write(4294967295, "25747: enabling fd 5\n", 21 
25747 write(4294967295, "25747: enabling fd 6\n", 21 
25747 write(4294967295, "25747: pausing fd 6\n", 20 
25747 write(4294967295, "25747: enabling fd 7\n", 21 
25747 write(4294967295, "25747: pausing fd 7\n", 20 
willy@wtap:haproxy$ grep 4294967295 log | grep 25748
25748 write(4294967295, "25748: enabling fd 4\n", 21 
25748 write(4294967295, "25748: pausing fd 4\n", 20 
25748 write(4294967295, "25748: enabling fd 5\n", 21 
25748 write(4294967295,

Re: nbproc 1 vs >1 performance

2016-04-14 Thread Willy Tarreau
On Thu, Apr 14, 2016 at 08:55:47AM +0200, Lukas Tribus wrote:
> Hi Willy,
> 
> 
> Am 14.04.2016 um 07:08 schrieb Willy Tarreau:
> >Hi Lukas,
> >
> >On Thu, Apr 14, 2016 at 12:14:15AM +0200, Lukas Tribus wrote:
> >>For example, the following configuration load balances the traffic across
> >>all 40 processes, expected or not?
> >>
> >>frontend haproxy_test
> >>  bind-process 1-40
> >>  bind :12345 process 1
> >
> >It's not expected. What is indicated above is that the frontend will
> >exist for the first 40 processes, and that port 12345 will be bound
> >only in process 1. Processes 2..40 will thus have no listener at all.
> 
> Well, we do only bind once here, not 40 times. But the traffic is then
> handled by all 40 processes.

No here it's handled by only one due to "bind :12345 process 1".

If there was not this "process 1", then it would indeed be handled by
all 40 processes in a random fashion.

> Le me put it this way:
> 
> frontend haproxy_test
>  bind-process 1-8
>  bind :12345 process 1
>  bind :12345 process 2
>  bind :12345 process 3
>  bind :12345 process 4
> 
> 
> Leads to 8 processes, and the master process binds the socket 4 times (PID
> 16509):
> 
> lukas@ubuntuvm:~/haproxy-1.5$ ps auxww | grep haproxy
> haproxy  16509  0.0  0.0  18460   320 ?Ss   08:41   0:00 ./haproxy
> -f ../cert/ruppert-nbproc-stress.cfg -D
> haproxy  16510  0.0  0.0  18460   320 ?Ss   08:41   0:00 ./haproxy
> -f ../cert/ruppert-nbproc-stress.cfg -D
> haproxy  16511  0.0  0.0  18460   320 ?Ss   08:41   0:00 ./haproxy
> -f ../cert/ruppert-nbproc-stress.cfg -D
> haproxy  16512  0.0  0.0  18460   320 ?Ss   08:41   0:00 ./haproxy
> -f ../cert/ruppert-nbproc-stress.cfg -D
> haproxy  16513  0.0  0.0  18460   320 ?Ss   08:41   0:00 ./haproxy
> -f ../cert/ruppert-nbproc-stress.cfg -D
> haproxy  16514  0.0  0.0  18460   320 ?Ss   08:41   0:00 ./haproxy
> -f ../cert/ruppert-nbproc-stress.cfg -D
> haproxy  16515  0.0  0.0  18460   320 ?Ss   08:41   0:00 ./haproxy
> -f ../cert/ruppert-nbproc-stress.cfg -D
> haproxy  16516  0.0  0.0  18460   320 ?Ss   08:41   0:00 ./haproxy
> -f ../cert/ruppert-nbproc-stress.cfg -D
> lukas@ubuntuvm:~/haproxy-1.5$ sudo netstat -tlp | grep hap
> tcp0  0 *:12345 *:* LISTEN  16509/haproxy
> tcp0  0 *:12345 *:* LISTEN  16509/haproxy
> tcp0  0 *:12345 *:* LISTEN  16509/haproxy
> tcp0  0 *:12345 *:* LISTEN  16509/haproxy
> lukas@ubuntuvm:~/haproxy-1.5$

Then this is where we have a problem. Only processes 1..4 should be bound.

> >But I'm rather surprized, maybe we recently broke something because
> >we have a lot of people successfully running such configurations,
> >which is why I'm a bit surprized.
> 
> I can reproduce this in v1.5.0 too, I think it was always like that.

I'm still very surprized because what this means is that each time we
managed to make a config work with such a setup it was by pure luck.
The most common use-case is with SSL and I'm aware of a significant
number of deployments running this way with the expected results.

I'll have to run some local tests to understand what's going on.

Thanks,
Willy



Re: nbproc 1 vs >1 performance

2016-04-13 Thread Lukas Tribus

Hi Willy,


Am 14.04.2016 um 07:08 schrieb Willy Tarreau:

Hi Lukas,

On Thu, Apr 14, 2016 at 12:14:15AM +0200, Lukas Tribus wrote:
  

For example, the following configuration load balances the traffic across
all 40 processes, expected or not?

frontend haproxy_test
  bind-process 1-40
  bind :12345 process 1


It's not expected. What is indicated above is that the frontend will
exist for the first 40 processes, and that port 12345 will be bound
only in process 1. Processes 2..40 will thus have no listener at all.


Well, we do only bind once here, not 40 times. But the traffic is then 
handled by all 40 processes.



Le me put it this way:

frontend haproxy_test
 bind-process 1-8
 bind :12345 process 1
 bind :12345 process 2
 bind :12345 process 3
 bind :12345 process 4


Leads to 8 processes, and the master process binds the socket 4 times 
(PID 16509):


lukas@ubuntuvm:~/haproxy-1.5$ ps auxww | grep haproxy
haproxy  16509  0.0  0.0  18460   320 ?Ss   08:41   0:00 
./haproxy -f ../cert/ruppert-nbproc-stress.cfg -D
haproxy  16510  0.0  0.0  18460   320 ?Ss   08:41   0:00 
./haproxy -f ../cert/ruppert-nbproc-stress.cfg -D
haproxy  16511  0.0  0.0  18460   320 ?Ss   08:41   0:00 
./haproxy -f ../cert/ruppert-nbproc-stress.cfg -D
haproxy  16512  0.0  0.0  18460   320 ?Ss   08:41   0:00 
./haproxy -f ../cert/ruppert-nbproc-stress.cfg -D
haproxy  16513  0.0  0.0  18460   320 ?Ss   08:41   0:00 
./haproxy -f ../cert/ruppert-nbproc-stress.cfg -D
haproxy  16514  0.0  0.0  18460   320 ?Ss   08:41   0:00 
./haproxy -f ../cert/ruppert-nbproc-stress.cfg -D
haproxy  16515  0.0  0.0  18460   320 ?Ss   08:41   0:00 
./haproxy -f ../cert/ruppert-nbproc-stress.cfg -D
haproxy  16516  0.0  0.0  18460   320 ?Ss   08:41   0:00 
./haproxy -f ../cert/ruppert-nbproc-stress.cfg -D

lukas@ubuntuvm:~/haproxy-1.5$ sudo netstat -tlp | grep hap
tcp0  0 *:12345 *:* LISTEN  16509/haproxy
tcp0  0 *:12345 *:* LISTEN  16509/haproxy
tcp0  0 *:12345 *:* LISTEN  16509/haproxy
tcp0  0 *:12345 *:* LISTEN  16509/haproxy
lukas@ubuntuvm:~/haproxy-1.5$



SO_REUSEPORT is almost irrelevant here. In fact it *happens* to perform
some load balancing, but the first use was simply to permit new processes
to rebind before unbinding older ones during reloads.


I know, haproxy unconditionally enables SO_REUSEPORT since 2006, because 
of OpenBSD reload/restarts (OpenBSD does not load-balance). But I'm not 
aware we ever changed anything after Linux introduced SO_REUSEPORT with 
load-balancing support in 3.9, so the fact that we bind everything to 
the master process is in my opinion simply based on the fact that 
SO_REUSEPORT when introduced was just a fix for reload/restart problems.





But I'm rather surprized, maybe we recently broke something because
we have a lot of people successfully running such configurations,
which is why I'm a bit surprized.


I can reproduce this in v1.5.0 too, I think it was always like that.




cheers,

lukas




Re: nbproc 1 vs >1 performance

2016-04-13 Thread Willy Tarreau
Hi Lukas,

On Thu, Apr 14, 2016 at 12:14:15AM +0200, Lukas Tribus wrote:
 
> For example, the following configuration load balances the traffic across
> all 40 processes, expected or not?
> 
> frontend haproxy_test
>  bind-process 1-40
>  bind :12345 process 1


It's not expected. What is indicated above is that the frontend will
exist for the first 40 processes, and that port 12345 will be bound
only in process 1. Processes 2..40 will thus have no listener at all.

> The docs [1] are not very clear and kind of contradict themselves:
> >does not enforce any process but eliminates those which do not match
> >[...]
> >If the frontend uses a "bind-process" setting, the intersection between
> >the
> >two is applied
> 
> 
> We mention in multiple places (docs, commits, ml) that we use the kernel's
> SO_REUSEPORT to loadbalance incoming traffic if supported, however due to
> the behavior mentioned above, when we do something like this:
> 
> frontend haproxy_test
>  bind :12345 process 1
>  bind :12345 process 2
>  bind :12345 process 3
>  bind :12345 process 4
> 
> 
> all those sockets are bound to the master process, not its forks:
> 
> lukas@ubuntuvm:~/haproxy$ ps auxww | grep haproxy
> haproxy  14457  0.0  0.1  16928   592 ?Ss   22:41   0:00 ./haproxy
> -D -f ../cert/ruppert-nbproc-stress.cfg
> haproxy  14458  0.0  0.1  16928   592 ?Ss   22:41   0:00 ./haproxy
> -D -f ../cert/ruppert-nbproc-stress.cfg
> haproxy  14459  0.0  0.1  16928   592 ?Ss   22:41   0:00 ./haproxy
> -D -f ../cert/ruppert-nbproc-stress.cfg
> haproxy  14460  0.0  0.1  16928   592 ?Ss   22:41   0:00 ./haproxy
> -D -f ../cert/ruppert-nbproc-stress.cfg
> lukas@ubuntuvm:~/haproxy$ sudo netstat -tlp | grep 12345
> tcp0  0 *:12345 *:* LISTEN  14457/haproxy
> tcp0  0 *:12345 *:* LISTEN  14457/haproxy
> tcp0  0 *:12345 *:* LISTEN  14457/haproxy
> tcp0  0 *:12345 *:* LISTEN  14457/haproxy
> lukas@ubuntuvm:~/haproxy$

Then there is a bug here.

> So I'm not sure how SO_REUSEPORT is supposed to load-balance between
> processes, if all sockets point to the master process (it will just pass the
> request around, like without SO_REUSEPORT). The docs [1] really suggest this
> configuration for SO_REUSEPORT based kernel side load-balancing.

SO_REUSEPORT is almost irrelevant here. In fact it *happens* to perform
some load balancing, but the first use was simply to permit new processes
to rebind before unbinding older ones during reloads.

The way SO_REUSEPORT works in the kernel is that it hashes ip and ports
and picks a socket queue based on the result of the hash. So any process
which is bound may be picked. That makes me realize that this behaviour
will definitely kill the principle of a master socket server by the way
since it will not be possible to keep a socket alive without taking one's
share of the traffic.

> It should look like this, imho:
> 
> lukas@ubuntuvm:~/haproxy$ sudo netstat -tlp | grep 12345
> tcp0  0 *:12345 *:* LISTEN  14713/haproxy
> tcp0  0 *:12345 *:* LISTEN  14712/haproxy
> tcp0  0 *:12345 *:* LISTEN  14711/haproxy
> tcp0  0 *:12345 *:* LISTEN  14710/haproxy
> lukas@ubuntuvm:~/haproxy$

Yes I agree.

> But that will only work across frontends:
> 
> frontend haproxy_test1
>  bind-process 1
>  bind :12345
>  default_backend backend_test
> frontend haproxy_test2
>  bind-process 2
>  bind :12345
>  default_backend backend_test
> frontend haproxy_test3
>  bind-process 3
>  bind :12345
>  default_backend backend_test
> frontend haproxy_test4
>  bind-process 4
>  bind :12345
>  default_backend backend_test

That's not normal. The principle of bind-process is that it limits the
processes on which a frontend stays enabled (in practice, it stops the
frontend after the fork on all processes where it's not bound). Then
the "process" directive on the "bind" line further refines this by
stopping the listeners on processes where they're not bound. Hence
the intersection mentionned in the doc.

But I'm rather surprized, maybe we recently broke something because
we have a lot of people successfully running such configurations,
which is why I'm a bit surprized.

Willy




Re: nbproc 1 vs >1 performance

2016-04-13 Thread Lukas Tribus

Hi Christian, Willy,


Am 13.04.2016 um 12:58 schrieb Christian Ruppert:


With the first config I get around ~30-33k requests/s on my test 
system, with the second conf (only the bind-process in the frontend 
section has been changed!) I just get around 26-28k requests per second.


I could get similar differences when playing with nbproc 1 and >1 as 
well as the default "bind-process" and/or the "process 1" on the 
actual bind.
Is it really just the multi process overhead causing the performance 
drop here, even tough the bind uses the first / only one process anyway?




With the "bind-process 1-40" in the frontend 
(haproxy-lessperformant.cfg), you are forcing haproxy to use all 40 
processes for this frontend traffic.


Therefor you get all the multi-process penalties (process 1 will load 
balance the requests to all 39 child processes). That's why performance 
sucks - single process configuration will benefit from your testcase 
because the cost of the error response is negligible.


With the "bind-process 1" in the frontend (inherited from the default 
section or the actual haproxy default: bind to the union of all the 
listeners' processes -  haproxy-moreperformant.cfg) just a single 
process is used for that frontend (and if no other processes are needed, 
no other processes will in fact be forked). Because your testcase favors 
single process mode, it outperforms multiple processes easily (no inter 
process overhead).



Try adding a dummy frontend to your moreperformant config:
frontend dummy_allproc
 bind-process 2-40
 bind :


You will see that haproxy_test frontend still performs fine, although 40 
processes are running, because only one process handles your ab test 
traffic (again no inter process overhead).



So that is what's happening here, the question is if this is really 
expected behavior.


For example, the following configuration load balances the traffic 
across all 40 processes, expected or not?


frontend haproxy_test
 bind-process 1-40
 bind :12345 process 1

The docs [1] are not very clear and kind of contradict themselves:

does not enforce any process but eliminates those which do not match
[...]
If the frontend uses a "bind-process" setting, the intersection 
between the

two is applied



We mention in multiple places (docs, commits, ml) that we use the 
kernel's SO_REUSEPORT to loadbalance incoming traffic if supported, 
however due to the behavior mentioned above, when we do something like this:


frontend haproxy_test
 bind :12345 process 1
 bind :12345 process 2
 bind :12345 process 3
 bind :12345 process 4


all those sockets are bound to the master process, not its forks:

lukas@ubuntuvm:~/haproxy$ ps auxww | grep haproxy
haproxy  14457  0.0  0.1  16928   592 ?Ss   22:41   0:00 
./haproxy -D -f ../cert/ruppert-nbproc-stress.cfg
haproxy  14458  0.0  0.1  16928   592 ?Ss   22:41   0:00 
./haproxy -D -f ../cert/ruppert-nbproc-stress.cfg
haproxy  14459  0.0  0.1  16928   592 ?Ss   22:41   0:00 
./haproxy -D -f ../cert/ruppert-nbproc-stress.cfg
haproxy  14460  0.0  0.1  16928   592 ?Ss   22:41   0:00 
./haproxy -D -f ../cert/ruppert-nbproc-stress.cfg

lukas@ubuntuvm:~/haproxy$ sudo netstat -tlp | grep 12345
tcp0  0 *:12345 *:* LISTEN  14457/haproxy
tcp0  0 *:12345 *:* LISTEN  14457/haproxy
tcp0  0 *:12345 *:* LISTEN  14457/haproxy
tcp0  0 *:12345 *:* LISTEN  14457/haproxy
lukas@ubuntuvm:~/haproxy$


So I'm not sure how SO_REUSEPORT is supposed to load-balance between 
processes, if all sockets point to the master process (it will just pass 
the request around, like without SO_REUSEPORT). The docs [1] really 
suggest this configuration for SO_REUSEPORT based kernel side 
load-balancing.



It should look like this, imho:

lukas@ubuntuvm:~/haproxy$ sudo netstat -tlp | grep 12345
tcp0  0 *:12345 *:* LISTEN  14713/haproxy
tcp0  0 *:12345 *:* LISTEN  14712/haproxy
tcp0  0 *:12345 *:* LISTEN  14711/haproxy
tcp0  0 *:12345 *:* LISTEN  14710/haproxy
lukas@ubuntuvm:~/haproxy$


But that will only work across frontends:

frontend haproxy_test1
 bind-process 1
 bind :12345
 default_backend backend_test
frontend haproxy_test2
 bind-process 2
 bind :12345
 default_backend backend_test
frontend haproxy_test3
 bind-process 3
 bind :12345
 default_backend backend_test
frontend haproxy_test4
 bind-process 4
 bind :12345
 default_backend backend_test



I think the process keyword on the bind line is either buggy or simply 
not designed to use with SO_REUSEPORT load-balancing, although the 
documentation suggest it.



Any insides here, Willy?



[1] http://cbonte.github.io/haproxy-dconv/configuration-1.6.html#5.1-process



nbproc 1 vs >1 performance

2016-04-13 Thread Christian Ruppert

Hi,

I've prepared a simple testcase:

haproxy-moreperformant.cfg:
global
nbproc 40
user haproxy
group haproxy
maxconn 175000

defaults
timeout client 300s
timeout server 300s
timeout queue 60s
timeout connect 7s
timeout http-request 10s
maxconn 175000

bind-process 1

frontend haproxy_test
#bind-process 1-40
bind :12345 process 1

mode http

default_backend backend_test

backend backend_test
mode http

errorfile 503 /etc/haproxy/test.error

# vim: set syntax=haproxy:


haproxy-lessperformant.cfg:
global
nbproc 40
user haproxy
group haproxy
maxconn 175000

defaults
timeout client 300s
timeout server 300s
timeout queue 60s
timeout connect 7s
timeout http-request 10s
maxconn 175000

bind-process 1

frontend haproxy_test
bind-process 1-40
bind :12345 process 1

mode http

default_backend backend_test

backend backend_test
mode http

errorfile 503 /etc/haproxy/test.error

# vim: set syntax=haproxy:

/etc/haproxy/test.error:
HTTP/1.0 200
Cache-Control: no-cache
Content-Type: text/plain

Test123456


The test:
ab -n 5000 -c 250 http://xx.xx.xx.xx:12345

With the first config I get around ~30-33k requests/s on my test system, 
with the second conf (only the bind-process in the frontend section has 
been changed!) I just get around 26-28k requests per second.


I could get similar differences when playing with nbproc 1 and >1 as 
well as the default "bind-process" and/or the "process 1" on the actual 
bind.
Is it really just the multi process overhead causing the performance 
drop here, even tough the bind uses the first / only one process anyway?


--
Regards,
Christian Ruppert