Difference between global maxconn and server maxconn haproxy

Willy got me an answer by email. I thought I would share it. His answers are in bold.

I have a question about my haproxy config:

   #---------------------------------------------------------------------
   # Global settings
   #---------------------------------------------------------------------
   global
       log         127.0.0.1 syslog emerg
       maxconn     4000
       quiet
       user        haproxy
       group       haproxy
       daemon
   #---------------------------------------------------------------------
   # common defaults that all the 'listen' and 'backend' sections will 
   # use if not designated in their block
   #---------------------------------------------------------------------
   defaults
       mode        http
       log         global
       option      abortonclose
       option      dontlognull
       option      httpclose
       option      httplog
       option      forwardfor
       option      redispatch
       timeout connect 10000 # default 10 second time out if a backend is not found
       timeout client 300000 # 5 min timeout for client
       timeout server 300000 # 5 min timeout for server
       stats       enable

   listen  http_proxy  localhost:81

       balance     roundrobin
       option      httpchk GET /empty.html
       server      server1 myip:80 maxconn 15 check inter 10000
       server      server2 myip:80 maxconn 15 check inter 10000

As you can see it is straight forward, but I am a bit confused about how the
maxconn properties work.

There is the global one and the maxconn on the server, in the listen block.

And there is also another one in the listen block which defaults to something
like 2000.

My thinking is this: the global one manages the total number of connections
that haproxy, as a service, will que or process at one time.

Correct. It’s the per-process max number of concurrent connections.

If the number
gets above that, it either kills the connection, or pools in some linux
socket?

The later, it simply stops accepting new connections and they remain in the
socket queue in the kernel. The number of queuable sockets is determined
by the min of (net.core.somaxconn, net.ipv4.tcp_max_syn_backlog, and the
listen block’s maxconn).

I have no idea what happens if the number gets higher than 4000.

The excess connections wait for another one to complete before being
accepted. However, as long as the kernel’s queue is not saturated, the
client does not even notice this, as the connection is accepted at the
TCP level but is not processed. So the client only notices some delay
to process the request.
But in practice, the listen block’s maxconn is much more important,
since by default it’s smaller than the global one. The listen’s maxconn
limits the number of connections per listener. In general it’s wise to
configure it for the number of connections you want for the service,
and to configure the global maxconn to the max number of connections
you let the haproxy process handle. When you have only one service,
both can be set to the same value. But when you have many services,
you can easily understand it makes a huge difference, as you don’t
want a single service to take all the connections and prevent the
other ones from working.

Then you have the server maxconn property set at 15. First off, I set that at
15 because my php-fpm, this is forwarding to on a separate server, only has
so many child processes it can use, so I make sure I am pooling the requests
here, instead of in php-fpm. Which I think is faster.

Yes, not only it should be faster, but it allows haproxy to find another
available server whenever possible, and also it allows it to kill the
request in the queue if the client hits “stop” before the connection is
forwarded to the server.

But back on the subject, my theory about this number is each server in this
block will only be sent 15 connections at a time. And then the connections
will wait for an open server. If I had cookies on, the connections would wait
for the CORRECT open server. But I don’t.

That’s exactly the principle. There is a per-proxy queue and a per-server
queue. Connections with a persistence cookie go to the server queue and
other connections go to the proxy queue. However since in your case no
cookie is configured, all connections go to the proxy queue. You can look
at the diagram doc/queuing.fig in haproxy sources if you want, it explains
how/where decisions are taken.

So questions are:

  1. What happens if the global connections get above 4000? Do they die? Or
    pool in Linux somehow?

    They’re queued in linux. Once you overwhelm the kernel’s queue, then they’re
    dropped in the kernel.

  2. Are the global connection related to the server connections, other than
    the fact you can’t have a total number of server connections greater than
    global?

    No, global and server connection settings are independant.

  3. When figuring out the global connections, shouldn’t it be the amount of
    connections added up in the server section, plus a certain percentage for
    pooling? And obviously you have other restrains on the connections, but
    really it is how many you want to send to the proxies?

    You got it right. If your server’s response time is short, there is nothing
    wrong with queueing thousands of connections to serve only a few at a time,
    because it substantially reduces the request processing time. Practically,
    establishing a connection nowadays takes about 5 microseconds on a gigabit
    LAN. So it makes a lot of sense to let haproxy distribute the connections
    as fast as possible from its queue to a server with a very small maxconn.
    I remember one gaming site queuing more than 30000 concurrent connections
    and running with a queue of 30 per server ! It was an apache server, and
    apache is much faster with small numbers of connections than with large
    numbers. But for this you really need a fast server, because you don’t
    want to have all your clients queued waiting for a connection slot because
    the server is waiting for a database for instance.
    Also something which works very well is to dedicate servers. If your site
    has many statics, you can direct the static requests to a pool of servers
    (or caches) so that you don’t queue static requests on them and that the
    static requests don’t eat expensive connection slots.
    Hoping this helps,
    Willy

Leave a Comment