HAProxy - Load balancer
Find a file
Willy Tarreau 2d7e3ddd4a
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
BUG/MEDIUM: cli: do not return ACKs one char at a time
Since 3.0 where the CLI started to use rcv_buf, it appears that some
external tools sending chained commands are randomly experiencing
failures. Each time this happens when the whole command is sent as a
single packet, immediately followed by a close. This is not a correct
way to use the CLI but this has been working for ages for simple
netcat-based scripts, so we should at least try to preserve this.

The cause of the failure is that the first LF that acks a command is
immediately sent back to the client and rejected due to the closed
connection. This in turn forwards the error back to the applet which
aborts its processing.

Before 3.0 the responses would be queued into the buffer, then sent
back to the channel, and would all fail at once. This changed when
snd_buf/rcv_buf were implemented because the applets are much more
responsive and since they yield between each command, they can
deliver one ACK at a time that is immediately forwarded down the
chain.

An easy way to observe the problem is to send 5 map updates, a shutdown,
and immediately close via tcploop, and in parallel run a periodic
"show map" to count the number of elements:

  $ tcploop -U /tmp/sock1 C S:"add map #0 1 1; add map #0 2 2; add map #0 3 3; add map #0 4 4; add map #0 5 5\n" F K

Before 3.0, there would always be 5 elements. Since 3.0 and before
20ec1de214 ("MAJOR: cli: Refacor parsing and execution of pipelined
commands"), almost always 2. And since that commit above in 3.2, almost
always one. Doing the same using socat or netcat shows almost always 5...
It's entirely timing-dependent, and might even vary based on the RTT
between the client and haproxy!

The approach taken here consists in doing the same principle as MSG_MORE
or Nagle but on the response buffer: the applet doesn't need to send a
single ACK for each command when it has already been woken up and is
scheduled to come back to work. It's fine (and even desirable) that
ACKs are grouped in a single packet as much as possible.

For this reason, this patch implements APPCTX_CLI_ST1_YIELD, a new CLI
flag which indicates that the applet left in yielding condition, i.e.
it has not finished its work. This flag is used by .rcv_buf to hold
pending data. This way we won't return partial responses for no reason,
and we can continue to emulate the previous behavior.

One very nice benefit to this is that it saves huge amounts of CPU on
the client. In the test below that tries to update 1M map entries, the
CPU used by socat went from 100% to 0% and the total transfer time
dropped by 28%:

  before:
    $ time awk 'BEGIN{ printf "prompt i\n"; for (i=0;i<1000000;i++) { \
         printf "add map #0 %d %d\n",i,i,i }}' | socat /tmp/sock1 - >/dev/null

    real    0m2.407s
    user    0m1.485s
    sys     0m1.682s

  after:
    $ time awk 'BEGIN{ printf "prompt i\n"; for (i=0;i<1000000;i++) { \
         printf "add map #0 %d %d\n",i,i,i }}' | socat /tmp/sock1 - >/dev/null

    real    0m1.721s
    user    0m0.952s
    sys     0m0.057s

The difference is also quite visible on the number of syscalls during
the test (for 1k updates):

  before:
    % time     seconds  usecs/call     calls    errors syscall
    ------ ----------- ----------- --------- --------- ----------------
    100.00    0.071691           0    100001           sendmsg

  after:
    % time     seconds  usecs/call     calls    errors syscall
    ------ ----------- ----------- --------- --------- ----------------
    100.00    0.000011           1         9           sendmsg

This patch will need to be backported to 3.0, and depends on these two
patches to be backported as well:

    MINOR: applet: do not put SE_FL_WANT_ROOM on rcv_buf() if the channel is empty
    MINOR: cli: create cli_raw_rcv_buf() from the generic applet_raw_rcv_buf()
2025-10-27 16:57:07 +01:00
.github CI: github: build halog on the vtest job 2025-09-26 16:29:29 +02:00
addons MINOR: applet: Add a flag to know an applet is using HTX buffers 2025-08-25 11:11:05 +02:00
admin ADMIN: reload: introduce -vv mode 2025-09-29 19:29:10 +02:00
dev DEV: patchbot: use git reset+checkout instead of pull 2025-10-08 04:38:35 +02:00
doc BUG/MINOR: quic: rename and duplicate stream settings 2025-10-23 16:49:20 +02:00
examples MINOR: mailers: warn if mailers are configured but not actually used 2025-06-27 16:41:18 +02:00
include BUG/MEDIUM: cli: do not return ACKs one char at a time 2025-10-27 16:57:07 +01:00
reg-tests REGTESTS: remove experimental from quic/retry.vtc 2025-10-17 20:55:43 +02:00
scripts CI: scripts: build curl with ECH support 2025-09-25 17:05:46 +02:00
src BUG/MEDIUM: cli: do not return ACKs one char at a time 2025-10-27 16:57:07 +01:00
tests TESTS: quic: add unit-tests for QUIC TX part 2025-09-08 14:49:03 +02:00
.cirrus.yml CI: cirrus-ci: bump FreeBSD image to 14-3 2025-10-09 14:06:48 +02:00
.gitattributes MINOR: Configure the cpp userdiff driver for *.[ch] in .gitattributes 2021-02-22 18:17:57 +01:00
.gitignore MINOR: tevt/dev: Add term_events tool 2025-01-31 10:41:50 +01:00
.mailmap DOC: update Tim's address in .mailmap 2021-09-16 09:14:14 +02:00
.travis.yml MEDIUM: mworker: remove USE_SYSTEMD requirement for -Ws 2024-11-20 12:07:38 +01:00
BRANCHES DOC: clarify the experimental status for certain features 2025-10-17 18:41:13 +02:00
BSDmakefile BUILD: makefile: commit the tiny FreeBSD makefile stub 2023-05-24 17:17:36 +02:00
CHANGELOG [RELEASE] Released version 3.3-dev10 2025-10-18 11:24:05 +02:00
CONTRIBUTING CLEANUP: assorted typo fixes in the code and comments 2025-04-02 11:12:20 +02:00
INSTALL BUILD: makefile: bump the default minimum linux version to 4.17 2025-09-05 09:44:56 +02:00
LICENSE LICENSE: add licence exception for OpenSSL 2012-09-07 13:52:26 +02:00
MAINTAINERS MAJOR: spoe: Let the SPOE back into the game 2024-05-22 09:04:38 +02:00
Makefile BUG/MEDIUM: build: limit excessive and counter-productive gcc-15 vectorization 2025-10-23 10:06:52 +02:00
README.md DOC: change the link to the FreeBSD CI in README.md 2024-06-03 15:21:29 +02:00
SUBVERS BUILD: use format tags in VERDATE and SUBVERS files 2013-12-10 11:22:49 +01:00
VERDATE [RELEASE] Released version 3.3-dev10 2025-10-18 11:24:05 +02:00
VERSION [RELEASE] Released version 3.3-dev10 2025-10-18 11:24:05 +02:00

HAProxy

alpine/musl AWS-LC openssl no-deprecated Illumos NetBSD FreeBSD VTest

HAProxy logo

HAProxy is a free, very fast and reliable reverse-proxy offering high availability, load balancing, and proxying for TCP and HTTP-based applications.

Installation

The INSTALL file describes how to build HAProxy. A list of packages is also available on the wiki.

Getting help

The discourse and the mailing-list are available for questions or configuration assistance. You can also use the slack or IRC channel. Please don't use the issue tracker for these.

The issue tracker is only for bug reports or feature requests.

Documentation

The HAProxy documentation has been split into a number of different files for ease of use. It is available in text format as well as HTML. The wiki is also meant to replace the old architecture guide.

Please refer to the following files depending on what you're looking for:

  • INSTALL for instructions on how to build and install HAProxy
  • BRANCHES to understand the project's life cycle and what version to use
  • LICENSE for the project's license
  • CONTRIBUTING for the process to follow to submit contributions

The more detailed documentation is located into the doc/ directory:

License

HAProxy is licensed under GPL 2 or any later version, the headers under LGPL 2.1. See the LICENSE file for a more detailed explanation.