Allow dynamic `use-server` rules in defaults sections (for backends created with `add backend ... from`) · Issue #3535 · haproxy/haproxy · GitHub
Skip to content

Allow dynamic use-server rules in defaults sections (for backends created with add backend ... from) #3535

Open

Description

@ygkat

Your Feature Request

Allow use-server in a named defaults section, at least when the server name is a log-format expression, so that backends created at runtime with add backend <name> from <defaults> can pin a request to a server.

Today it is rejected. Reproduced with an unmodified 3.4.6 build, and the same check is still in master (3.5-dev8):

defaults tpl
    mode http
    balance roundrobin
    timeout connect 5s
    timeout client 30s
    timeout server 30s
    http-request set-timeout server 10s
    use-server %[req.hdr(x-pin-server)] if { req.hdr(x-pin-server) -m found }

frontend fe
    bind 127.0.0.1:18080
    use_backend %[req.hdr(x-be)]
[ALERT] config : parsing [repro.cfg:8] : 'use-server' not allowed in 'defaults' section.

A runtime backend has no rules of its own and gets everything from the defaults it references, so there is currently no way to give it a use-server rule.

From a quick look at the code, a possible approach:

  • Accept use-server in defaults only with a dynamic (log-format) name. Static names are resolved to a server per proxy at config check time, which cannot be shared, while dynamic names are already looked up in s->be for each stream.
  • In process_server_rules(), evaluate px->defpx->server_rules before px->server_rules, as http_process_req_common() does for http-request rules.
  • Count server_rules as rules in proxy_ref_defaults(), so the existing restriction on sharing a defaults section with rules between frontends and backends applies.
  • Set AN_REQ_SRV_RULES on backends that reference such a defaults section.

What are you trying to do?

We create backends at runtime with add backend ... from <template> followed by add server, using one named defaults section per application type. All of our backends built from configuration files carry a header-based override that pins a request to one server for deployment tests:

use-server %[req.hdr(x-pin-server)] if { req.hdr(x-pin-server) -m found }

It is the only line of our backend template that cannot move to a defaults section. Backends created at runtime lose it until a reload rebuilds them from the configuration.

We also considered routing pinned requests to a separate backend holding only the target server, but it does not fit well:

  • Every server needs a twin backend, which roughly doubles the number of proxies, and creating one application backend at runtime becomes 1+N add backend/publish operations with no atomicity between them.
  • The twin servers need their own health checks, because track cannot reference a dynamic server ("unable to use ... for tracking as it is a dynamic server"). This doubles check traffic to the origins, and the two copies can briefly disagree on state.
  • Every runtime change (add/del server, weight, maint, drain) has to be applied to both copies.
  • maxconn, queues, idle connection pools and counters are per server object, so pinned traffic bypasses the main server's limits and does not reuse its connections.
  • use-server falls back to load balancing when the pinned server is down. A twin backend needs extra frontend rules (e.g. the srv_is_up converter) to do the same.
  • use-server only looks up servers of the backend already chosen by host/path routing. Building a backend name from a request header in the frontend has to be restricted carefully so that a client cannot reach another application's backend.

Output of haproxy -vv

HAProxy version 3.4.6-56332c5 2026/09/28 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2031.
Known bugs: http://www.haproxy.org/bugs/bugs-3.4.6.html
Build options : 
  TARGET  = linux-glibc
  CC      = cc
  CFLAGS  = -O2 -g -fwrapv -fvect-cost-model=very-cheap
  OPTIONS = 
  DEBUG   = 

Feature list : -51DEGREES +ACCEPT4 +BACKTRACE -CLOSEFROM +CPU_AFFINITY +CRYPT_H -DEVICEATLAS +DL -ECH -ENGINE +EPOLL -EVPORTS +GETADDRINFO +HAVE_TCP_MD5SIG -KQUEUE +KTLS -LIBATOMIC +LIBCRYPT +LINUX_CAP +LINUX_SPLICE +LINUX_TPROXY -LUA -MATH -MEMORY_PROFILING +NETFILTER +NS -OBSOLETE_LINKER -OPENSSL -OPENSSL_AWSLC -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 +TFO +THREAD +THREAD_DUMP +TPROXY +TRACE -WURFL -ZLIB
Detected feature list : +HAVE_WORKING_TCP_MD5SIG

Default settings :
  bufsize = 16384, maxrewrite = 1024, maxpollevents = 200

Built with multi-threading support (MAX_TGROUPS=32, MAX_THREADS=1024, default=8).
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 without PCRE or PCRE2 support (using libc's regex instead)
Encrypted password support via crypt(3): yes
Built with gcc compiler version 11.5.0 20240719 (Red Hat 11.5.0-14)

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
  <default> : mode=HTTP  side=FE|BE  mux=H1    flags=HTX
         h1 : mode=HTTP  side=FE|BE  mux=H1    flags=HTX|NO_UPG
       fcgi : mode=HTTP  side=BE     mux=FCGI  flags=HTX|HOL_RISK|NO_UPG
  <default> : mode=SPOP  side=BE     mux=SPOP  flags=HOL_RISK|NO_UPG
       spop : mode=SPOP  side=BE     mux=SPOP  flags=HOL_RISK|NO_UPG
  <default> : mode=TCP   side=FE|BE  mux=PASS  flags=
       none : mode=TCP   side=FE|BE  mux=PASS  flags=NO_UPG

Available services : none

Available filters :
	[BWLIM] bwlim-in
	[BWLIM] bwlim-out
	[CACHE] cache
	[COMP] comp-req
	[COMP] comp-res
	[COMP] compression
	[FCGI] fcgi-app
	[SPOE] spoe
	[TRACE] trace

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions