]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: check: implement check-reuse-pool
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 28 Mar 2025 16:25:57 +0000 (17:25 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 2 Apr 2025 12:57:40 +0000 (14:57 +0200)
Implement the possibility to reuse idle connections when performing
server checks. This is done thanks to the recently introduced functions
be_calculate_conn_hash() and be_reuse_connection().

One side effect of this change is that be_calculate_conn_hash() can now
be called with a NULL stream instance. As such, part of the functions
are adjusted accordingly.

Note that to simplify configuration, connection reuse is not performed
if any specific check connection parameters are defined on the server
line or via the tcp-check connect rule. This is performed via newly
defined tcpcheck_use_nondefault_connect().

include/haproxy/tcpcheck-t.h
src/backend.c
src/tcpcheck.c

index 22310eee052daf927ab806e028360e10253b4795..f01d512ebf4be2fd1d9df8b5a65d58ed9ab852d7 100644 (file)
@@ -36,6 +36,7 @@
 #define TCPCHK_OPT_IMPLICIT        0x0010  /* Implicit connect */
 #define TCPCHK_OPT_SOCKS4          0x0020  /* check the connection via socks4 proxy */
 #define TCPCHK_OPT_HAS_DATA        0x0040  /* data should be sent after connection */
+#define TCPCHK_MASK_OPTS_CONNECT   0x0027  /* mask for any options which overrides default connect params */
 
 enum tcpcheck_send_type {
        TCPCHK_SEND_UNDEF = 0,  /* Send is not parsed. */
index 4b3eeec2a3dea0318e699d46828e22ec6efa00ff..80b651510fa4016b1bc6366fbf3909bbc85299ac 100644 (file)
@@ -1547,11 +1547,14 @@ static int be_reuse_mode(struct proxy *be, struct server *srv)
 /* Calculate hash to select a matching connection for reuse. Here is the list
  * of input parameters :
  * - <srv> is the server instance. Can be NULL on dispatch/transparent proxy.
- * - <strm> is the stream instance.
+ * - <strm> is the stream instance. Can be NULL if no stream is used.
  * - <src> is the bind address if an explicit source address is used.
  * - <dst> is the destination address. Must be set in every cases, except on
  *   reverse HTTP.
  *
+ * Note that all input parameters can be NULL. The only requirement is that
+ * it's not possible to have both <srv> and <strm> NULL at the same time.
+ *
  * Returns the calculated hash.
  */
 int64_t be_calculate_conn_hash(struct server *srv, struct stream *strm,
@@ -1592,7 +1595,7 @@ int64_t be_calculate_conn_hash(struct server *srv, struct stream *strm,
        hash_params.src_addr = src;
 
        /* 5. proxy protocol */
-       if (srv && srv->pp_opts & SRV_PP_ENABLED) {
+       if (strm && srv && srv->pp_opts & SRV_PP_ENABLED) {
                struct connection *cli_conn = objt_conn(strm_orig(strm));
                int proxy_line_ret = make_proxy_line(trash.area, trash.size,
                                                     srv, cli_conn, strm, sess);
@@ -1603,7 +1606,7 @@ int64_t be_calculate_conn_hash(struct server *srv, struct stream *strm,
        }
 
        /* 6. Custom mark, tos? */
-       if (strm->flags & (SF_BC_MARK | SF_BC_TOS)) {
+       if (strm && (strm->flags & (SF_BC_MARK | SF_BC_TOS))) {
                /* mark: 32bits, tos: 8bits = 40bits
                 * last 2 bits are there to indicate if mark and/or tos are set
                 * total: 42bits:
index a366b9e59b4ac378a2286f84275c756753867bde..754f41448ea142671febb1b657190a46fee4d020 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <haproxy/action.h>
 #include <haproxy/api.h>
+#include <haproxy/backend.h>
 #include <haproxy/cfgparse.h>
 #include <haproxy/check.h>
 #include <haproxy/chunk.h>
@@ -1206,6 +1207,20 @@ enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct t
        goto out;
 }
 
+/* Returns true if <check> or <connect> rule uses any specific connect options
+ * which may differ from their underlying server counterparts.
+ */
+static inline int tcpcheck_use_nondefault_connect(const struct check *check,
+                                                  const struct tcpcheck_connect *connect)
+{
+       return check->mux_proto || connect->mux_proto ||
+         is_addr(&check->addr) || is_addr(&connect->addr) ||
+         check->port || connect->port || connect->port_expr ||
+         check->use_ssl || check->sni || connect->sni || check->alpn_len || connect->alpn_len ||
+         check->send_proxy || check->via_socks4 ||
+         (connect->options & TCPCHK_MASK_OPTS_CONNECT);
+}
+
 /* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
  * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
  * TCPCHK_EVAL_STOP if an error occurred.
@@ -1249,6 +1264,32 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
        check_release_buf(check, &check->bi);
        check_release_buf(check, &check->bo);
 
+       if (!(check->state & CHK_ST_AGENT) && check->reuse_pool &&
+           !tcpcheck_use_nondefault_connect(check, connect)) {
+               int64_t hash;
+               int conn_err;
+
+               TRACE_DEVEL("trying connection reuse for check", CHK_EV_TCPCHK_CONN, check);
+
+               hash = be_calculate_conn_hash(s, NULL, check->sess, NULL, NULL);
+               conn_err = be_reuse_connection(hash, check->sess, s->proxy, s,
+                                              check->sc, &s->obj_type, 0);
+               if (conn_err == SF_ERR_INTERNAL) {
+                       TRACE_ERROR("error during connection reuse", CHK_EV_TCPCHK_CONN|CHK_EV_TCPCHK_ERR, check);
+                       set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
+                       ret = TCPCHK_EVAL_STOP;
+                       goto out;
+               }
+               else if (conn_err == SF_ERR_NONE) {
+                       TRACE_STATE("check performed with connection reuse", CHK_EV_TCPCHK_CONN, check);
+                       conn = __sc_conn(check->sc);
+                       conn_set_owner(conn, check->sess, NULL);
+                       /* connection will be reinsert in idle conn pool due to missing conn_set_private(). */
+                       status = SF_ERR_NONE;
+                       goto skip_connect;
+               }
+       }
+
        /* No connection, prepare a new one */
        conn = conn_new((s ? &s->obj_type : &proxy->obj_type));
        if (!conn) {
@@ -1414,6 +1455,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
        }
 
   fail_check:
+  skip_connect:
        /* It can return one of :
         *  - SF_ERR_NONE if everything's OK
         *  - SF_ERR_SRVTO if there are no more servers