Detailed Description of the Problem
We have an HAProxy deployment where haproxy daemonset instances are used to load balance HTTP2 requests onto our backend servers.
Our backend servers limit the maximum concurrent stream count to 3.
The HAProxy config we use has the maximum concurrent streams count set to 1000 (tune.h2.max-concurrent-streams 1000).
Despite haproxy and the backend server both exchanging their SETTINGS and acknowledging them, in some rare instances, haproxy ignores the backend limit and sends more than 3 HTTP2 streams. This results in the backend server terminating the connection shortly afterwards.
Not allowed to include .pcap attachments, attaching screenshots of the behavior instead.
3480 is the backend port, 52324 is the haproxy client port for this connection
Overview of the entire TCP stream:

HAProxy SETTINGS frame (max streams 1000)

Backend server SETTINGS frame (max streams 3)

Backend SETTINGS ACK

HAProxy SETTINGS ACK

4 distinct HTTP streams sent by HAProxy

Expected Behavior
We would expect HAProxy to follow the backend server limitation on the max number of concurrent HTTP2 streams.
Steps to Reproduce the Behavior
We can only reproduce this at a customer lab, under heavy load, and even in that environment it only occurs sporadically.
Do you have any idea what may have caused this?
No response
Do you have an idea how to solve the issue?
Setting the tune.h2.be.max-concurrent-streams explicitly to 3 in the HAProxy config seems to resolve the issue.
What is your configuration?
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# # HAProxy Ingress Controller
# # --------------------------
# # This file is automatically updated, do not edit
# #
#
global
master-worker
unix-bind mode 0600
stats socket /var/run/haproxy/admin.sock level admin expose-fd listeners mode 600
maxconn 100000
hard-stop-after 10m
log 127.0.0.1:514 len 1024 format rfc5424 local0
log-tag ingress
lua-load /etc/haproxy/lua/services.lua
lua-load /etc/haproxy/lua/responses.lua
tune.ssl.default-dh-param 2048
lua-prepend-path /etc/REDACTED/?.lua
lua-load /etc/REDACTED/XXX.lua
tune.h2.max-concurrent-streams 1000
tune.h2.initial-window-size 1048576
tune.bufsize 131072
nbthread 1
defaults
log global
maxconn 100000
option redispatch
option dontlognull
option http-server-close
option http-keep-alive
timeout client 50s
timeout client-fin 50s
timeout connect 5s
timeout http-keep-alive 1m
timeout http-request 5s
timeout queue 5s
timeout server 50s
timeout server-fin 50s
timeout tunnel 1h
option http-no-delay
timeout connect 200
timeout client 300s
timeout server 5000
option dontlog-normal
no option prefer-last-server
timeout client-fin 1s
timeout server-fin 1s
no option http-server-close
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# # BACKENDS
# #
#
backend XXX_west-site1-ingress-local_3480
mode http
balance leastconn
acl https-request ssl_fc
http-request set-header X-Original-Forwarded-For %[hdr(x-forwarded-for)] if { hdr(x-forwarded-for) -m found }
http-request del-header x-forwarded-for
option forwardfor
http-reuse always
option nolinger
option httpchk
http-check connect proto h2
http-check send meth HEAD uri http://%s/oam/v1 ver HTTP/2 hdr XXX-health-check true
http-check expect rstatus [0-9]+
default-server max-reuse 500 fall 1 rise 5
retries 3
retry-on conn-failure empty-response
option redispatch
Output of haproxy -vv
# haproxy -vv
HAProxy version 2.8.5-aaba8d0 2023/12/07 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2028.
Known bugs: http://www.haproxy.org/bugs/bugs-2.8.5.html
Running on: Linux 4.18.0-348.12.2.el8_5.x86_64 #1 SMP Mon Jan 17 07:06:06 EST 2022 x86_64
Build options :
TARGET = linux-glibc
CPU = generic
CC = cc
CFLAGS = -O2 -g -Wall -Wextra -Wundef -Wdeclaration-after-statement -Wfatal-errors -Wtype-limits -Wshift-negative-value -Wshift-overflow=2 -Wduplicated-cond -Wnull-dereference -fwrapv -Wno-address-of-packed-member -Wno-unused-label -Wno-sign-compare -Wno-unused-parameter -Wno-clobbered -Wno-missing-field-initializers -Wno-cast-function-type -Wno-string-plus-int -Wno-atomic-alignment
OPTIONS = USE_GETADDRINFO=1 USE_OPENSSL=1 USE_LUA=1 USE_PROMEX=1 USE_PCRE2=1 USE_PCRE2_JIT=1
DEBUG = -DDEBUG_STRICT -DDEBUG_MEMORY_POOLS
Feature list : -51DEGREES +ACCEPT4 +BACKTRACE -CLOSEFROM +CPU_AFFINITY +CRYPT_H -DEVICEATLAS +DL -ENGINE +EPOLL -EVPORTS +GETADDRINFO -KQUEUE -LIBATOMIC +LIBCRYPT +LINUX_CAP +LINUX_SPLICE +LINUX_TPROXY +LUA +MATH -MEMORY_PROFILING +NETFILTER +NS -OBSOLETE_LINKER +OPENSSL -OPENSSL_WOLFSSL -OT -PCRE +PCRE2 +PCRE2_JIT -PCRE_JIT +POLL +PRCTL -PROCCTL +PROMEX -PTHREAD_EMULATION -QUIC -QUIC_OPENSSL_COMPAT +RT +SHM_OPEN +SLZ +SSL -STATIC_PCRE -STATIC_PCRE2 -SYSTEMD +TFO +THREAD +THREAD_DUMP +TPROXY -WURFL -ZLIB
Default settings :
bufsize = 16384, maxrewrite = 1024, maxpollevents = 200
Built with multi-threading support (MAX_TGROUPS=16, MAX_THREADS=256, default=12).
Built with OpenSSL version : OpenSSL 1.1.1w 11 Sep 2023
Running on OpenSSL version : OpenSSL 1.1.1w 11 Sep 2023
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports : TLSv1.0 TLSv1.1 TLSv1.2 TLSv1.3
Built with Lua version : Lua 5.3.3
Built with the Prometheus exporter as a service
Built with network namespace support.
Built with libslz for stateless compression.
Compression algorithms supported : identity("identity"), deflate("deflate"), raw-deflate("deflate"), gzip("gzip")
Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT IP_FREEBIND
Built with PCRE2 version : 10.36 2020-12-04
PCRE2 library supports JIT : yes
Encrypted password support via crypt(3): yes
Built with gcc compiler version 10.2.1 20210110
Available polling systems :
epoll : pref=300, test result OK
poll : pref=200, test result OK
select : pref=150, test result OK
Total: 3 (3 usable), will use epoll.
Available multiplexer protocols :
(protocols marked as <default> cannot be specified using 'proto' keyword)
h2 : mode=HTTP side=FE|BE mux=H2 flags=HTX|HOL_RISK|NO_UPG
fcgi : mode=HTTP side=BE mux=FCGI flags=HTX|HOL_RISK|NO_UPG
<default> : mode=HTTP side=FE|BE mux=H1 flags=HTX
h1 : mode=HTTP side=FE|BE mux=H1 flags=HTX|NO_UPG
<default> : mode=TCP side=FE|BE mux=PASS flags=
none : mode=TCP side=FE|BE mux=PASS flags=NO_UPG
Available services : prometheus-exporter
Available filters :
[BWLIM] bwlim-in
[BWLIM] bwlim-out
[CACHE] cache
[COMP] compression
[FCGI] fcgi-app
[SPOE] spoe
[TRACE] trace
Last Outputs and Backtraces
Additional Information
No response
Detailed Description of the Problem
We have an HAProxy deployment where haproxy daemonset instances are used to load balance HTTP2 requests onto our backend servers.
Our backend servers limit the maximum concurrent stream count to 3.
The HAProxy config we use has the maximum concurrent streams count set to 1000 (tune.h2.max-concurrent-streams 1000).
Despite haproxy and the backend server both exchanging their SETTINGS and acknowledging them, in some rare instances, haproxy ignores the backend limit and sends more than 3 HTTP2 streams. This results in the backend server terminating the connection shortly afterwards.
Not allowed to include .pcap attachments, attaching screenshots of the behavior instead.
3480 is the backend port, 52324 is the haproxy client port for this connection
Overview of the entire TCP stream:

HAProxy SETTINGS frame (max streams 1000)

Backend server SETTINGS frame (max streams 3)

Backend SETTINGS ACK

HAProxy SETTINGS ACK
4 distinct HTTP streams sent by HAProxy

Expected Behavior
We would expect HAProxy to follow the backend server limitation on the max number of concurrent HTTP2 streams.
Steps to Reproduce the Behavior
We can only reproduce this at a customer lab, under heavy load, and even in that environment it only occurs sporadically.
Do you have any idea what may have caused this?
No response
Do you have an idea how to solve the issue?
Setting the tune.h2.be.max-concurrent-streams explicitly to 3 in the HAProxy config seems to resolve the issue.
What is your configuration?
Output of
haproxy -vvLast Outputs and Backtraces
Additional Information
No response