]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: session: account on server idle conns attached to session
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 8 Aug 2025 13:56:47 +0000 (15:56 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 28 Aug 2025 13:08:35 +0000 (15:08 +0200)
This patch adds a new member <curr_sess_idle_conns> on the server. It
serves as a counter of idle connections attached on a session instead of
regular idle/safe trees. This is used only for private connections.

The objective is to provide a method to detect if there is idle
connections still referencing a server.

This will be particularly useful to ensure that a server is removable.
Currently, this is not yet necessary as idle connections are directly
freed via "del server" handler under thread isolation. However, this
procedure will be replaced by an asynchronous mechanism outside of
thread isolation.

Careful: connections attached to a session but not idle will not be
accounted by this counter. These connections can still be detected via
srv_has_streams() so "del server" will be safe.

This counter is maintain during the whole lifetime of a private
connection. This is mandatory to guarantee "del server" safety and is
conform with other idle server counters. What this means it that
decrement is performed only when the connection transitions from idle to
in use, or just prior to its deletion. For the first case, this is
covered by session_get_conn(). The second case is trickier. It cannot be
done via session_unown_conn() as a private connection may still live a
little longer after its removal from session, most notably when
scheduled for idle purging.

Thus, conn_free() has been adjusted to handle the final decrement. Now,
conn_backend_deinit() is also called for private connections if
CO_FL_SESS_IDLE flag is present. This results in a call to
srv_release_conn() which is responsible to decrement server idle
counters.

include/haproxy/server-t.h
src/connection.c
src/server.c
src/session.c

index 85dcd63da2dbef44b15a929a2a8523dd9ebe5c9f..dd76ffac6aeecb82de3641645c43072cdcb40897 100644 (file)
@@ -388,6 +388,7 @@ struct server {
        unsigned int curr_total_conns;          /* Current number of total connections to the server, used or idle, only calculated if strict-maxconn is used */
        unsigned int max_used_conns;            /* Max number of used connections (the counter is reset at each connection purges */
        unsigned int est_need_conns;            /* Estimate on the number of needed connections (max of curr and previous max_used) */
+       unsigned int curr_sess_idle_conns;      /* Current number of idle connections attached to a session instead of idle/safe trees. */
 
        /* Element below are usd by LB algorithms and must be doable in
         * parallel to other threads reusing connections above.
index d1faf6f2af460850999c5371c23fe07dd4e70f4b..4f0b5c342b7120ef0e1250ec86e972029c4e171b 100644 (file)
@@ -540,10 +540,9 @@ static void conn_backend_deinit(struct connection *conn)
        if (obj_type(conn->target) == OBJ_TYPE_SERVER) {
                struct server *srv = __objt_server(conn->target);
 
-               /* If the connection is not private, it is accounted by the server. */
-               if (!(conn->flags & CO_FL_PRIVATE)) {
+               if (!(conn->flags & CO_FL_PRIVATE) || (conn->flags & CO_FL_SESS_IDLE))
                        srv_release_conn(srv, conn);
-               }
+
                if (srv->flags & SRV_F_STRICT_MAXCONN)
                        _HA_ATOMIC_DEC(&srv->curr_total_conns);
        }
index 9c120171f14088ee3f97f093053948404cf57813..6599e53d7d162c82bfdaeb1459d9752b7b77b4cb 100644 (file)
@@ -7251,7 +7251,11 @@ static void srv_cleanup_connections(struct server *srv)
 /* removes an idle conn after updating the server idle conns counters */
 void srv_release_conn(struct server *srv, struct connection *conn)
 {
-       if (conn->flags & CO_FL_LIST_MASK) {
+       if (conn->flags & CO_FL_SESS_IDLE) {
+               _HA_ATOMIC_DEC(&srv->curr_sess_idle_conns);
+               conn->flags &= ~CO_FL_SESS_IDLE;
+       }
+       else if (conn->flags & CO_FL_LIST_MASK) {
                /* The connection is currently in the server's idle list, so tell it
                 * there's one less connection available in that list.
                 */
index 43e54dc5ee5c214df4a995394bcf542454215546..103fb159ae59420fb78e206a6b71de5068570c29 100644 (file)
@@ -139,7 +139,6 @@ void session_free(struct session *sess)
                list_for_each_entry_safe(conn, conn_back, &pconns->conn_list, sess_el) {
                        LIST_DEL_INIT(&conn->sess_el);
                        conn->owner = NULL;
-                       conn->flags &= ~CO_FL_SESS_IDLE;
                        LIST_APPEND(&conn_tmp_list, &conn->sess_el);
                }
                MT_LIST_DELETE(&pconns->srv_el);
@@ -732,6 +731,9 @@ int session_reinsert_idle_conn(struct session *sess, struct connection *conn)
 /* Check that session <sess> is able to keep idle connection <conn>. This must
  * be called each time a connection stored in a session becomes idle.
  *
+ * If <conn> can be kept as idle in the session, idle sess conn counter of its
+ * target server will be incremented.
+ *
  * Returns 0 if the connection is kept, else non-zero if the connection was
  * explicitely removed from session.
  */
@@ -758,6 +760,8 @@ int session_check_idle_conn(struct session *sess, struct connection *conn)
        else {
                conn->flags |= CO_FL_SESS_IDLE;
                sess->idle_conns++;
+               if (srv)
+                       HA_ATOMIC_INC(&srv->curr_sess_idle_conns);
        }
 
        return 0;
@@ -771,6 +775,7 @@ struct connection *session_get_conn(struct session *sess, void *target, int64_t
 {
        struct connection *srv_conn, *res = NULL;
        struct sess_priv_conns *pconns;
+       struct server *srv;
 
        HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 
@@ -787,6 +792,10 @@ struct connection *session_get_conn(struct session *sess, void *target, int64_t
                        if (srv_conn->flags & CO_FL_SESS_IDLE) {
                                srv_conn->flags &= ~CO_FL_SESS_IDLE;
                                sess->idle_conns--;
+
+                               srv = objt_server(srv_conn->target);
+                               if (srv)
+                                       HA_ATOMIC_DEC(&srv->curr_sess_idle_conns);
                        }
 
                        res = srv_conn;