]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MAJOR: server: implement purging of private idle connections
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 8 Aug 2025 15:13:43 +0000 (17:13 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 28 Aug 2025 13:08:35 +0000 (15:08 +0200)
When a server goes into maintenance, or if its IP address is changed,
idle connections attached to it are scheduled for deletion via the purge
mechanism. Connections are moved from server idle/safe list to the purge
list relative to their thread. Connections are freed on their owned
thread by the scheduled purge task.

This patch extends this procedure to also handle private idle
connections stored in sessions instead of servers. This is possible
thanks via <sess_conns> list server member. A call to the newly
defined-function session_purge_conns() is performed on each list
element. This moves private connections from their session to the purge
list alongside other server idle connections.

This change relies on the serie of previous commits which ensure that
access to private idle connections is now thread-safe, with idle_conns
lock usage and careful manipulation of private idle conns in
input/output handlers.

The main benefit of this patch is that now all idle connections
targetting a server set in maintenance are removed. Previously, private
connections would remain until their attach sessions were closed.

include/haproxy/session.h
src/server.c
src/session.c

index 7014a8637e77f311d88636942affe2aaddc95587..677300ca74f9fc422a6b00a4f12944da18f42743 100644 (file)
@@ -49,6 +49,7 @@ int session_check_idle_conn(struct session *sess, struct connection *conn);
 struct connection *session_get_conn(struct session *sess, void *target, int64_t hash);
 void session_unown_conn(struct session *sess, struct connection *conn);
 int session_detach_idle_conn(struct session *sess, struct connection *conn);
+int sess_conns_cleanup_all_idle(struct sess_priv_conns *sess_conns);
 
 /* Remove the refcount from the session to the tracked counters, and clear the
  * pointer to ensure this is only performed once. The caller is responsible for
index 88e85b724ec126ccd7572114fe68fb995602ff02..9c120171f14088ee3f97f093053948404cf57813 100644 (file)
@@ -7210,6 +7210,7 @@ static int srv_migrate_conns_to_remove(struct list *list, struct mt_list *toremo
  */
 static void srv_cleanup_connections(struct server *srv)
 {
+       struct sess_priv_conns *sess_conns;
        int did_remove;
        int i;
 
@@ -7221,8 +7222,23 @@ static void srv_cleanup_connections(struct server *srv)
        for (i = tid;;) {
                did_remove = 0;
                HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
+
+               /* idle connections */
                if (srv_migrate_conns_to_remove(&srv->per_thr[i].idle_conn_list, &idle_conns[i].toremove_conns, -1) > 0)
                        did_remove = 1;
+
+               /* session attached connections */
+               while ((sess_conns = MT_LIST_POP(&srv->per_thr[i].sess_conns, struct sess_priv_conns *, srv_el))) {
+                       if (sess_conns_cleanup_all_idle(sess_conns)) {
+                               did_remove = 1;
+
+                               if (LIST_ISEMPTY(&sess_conns->conn_list)) {
+                                       LIST_DELETE(&sess_conns->sess_el);
+                                       pool_free(pool_head_sess_priv_conns, sess_conns);
+                               }
+                       }
+               }
+
                HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
                if (did_remove)
                        task_wakeup(idle_conns[i].cleanup_task, TASK_WOKEN_OTHER);
index cad66ddc6ea8a5e2fa5a7b6ccb2c74cb6cd1f398..43e54dc5ee5c214df4a995394bcf542454215546 100644 (file)
@@ -874,6 +874,34 @@ int session_detach_idle_conn(struct session *sess, struct connection *conn)
        return 1;
 }
 
+/* Remove every idle backend connections stored in <sess_conns> and move them
+ * into the purge list. If <sess_conns> is empty it is also removed from the
+ * session and freed.
+ *
+ * Returns the number of connections moved to purge list.
+ */
+int sess_conns_cleanup_all_idle(struct sess_priv_conns *sess_conns)
+{
+       struct connection *conn, *back;
+       int conn_tid = sess_conns->tid;
+       int i = 0;
+
+       list_for_each_entry_safe(conn, back, &sess_conns->conn_list, sess_el) {
+               if (!(conn->flags & CO_FL_SESS_IDLE))
+                       continue;
+
+               /* Decrement session idle counter. */
+               --((struct session *)conn->owner)->idle_conns;
+
+               LIST_DEL_INIT(&conn->sess_el);
+               MT_LIST_APPEND(&idle_conns[conn_tid].toremove_conns,
+                              &conn->toremove_list);
+               ++i;
+       }
+
+       return i;
+}
+
 
 /*
  * Local variables: