From: Amaury Denoyelle Date: Fri, 8 Aug 2025 15:13:43 +0000 (+0200) Subject: MAJOR: server: implement purging of private idle connections X-Git-Tag: v3.3-dev8~73 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7a6e3c1a73a6f86e9531f9c738c982fdc86a4336;p=thirdparty%2Fhaproxy.git MAJOR: server: implement purging of private idle connections 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 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. --- diff --git a/include/haproxy/session.h b/include/haproxy/session.h index 7014a8637..677300ca7 100644 --- a/include/haproxy/session.h +++ b/include/haproxy/session.h @@ -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 diff --git a/src/server.c b/src/server.c index 88e85b724..9c120171f 100644 --- a/src/server.c +++ b/src/server.c @@ -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); diff --git a/src/session.c b/src/session.c index cad66ddc6..43e54dc5e 100644 --- a/src/session.c +++ b/src/session.c @@ -874,6 +874,34 @@ int session_detach_idle_conn(struct session *sess, struct connection *conn) return 1; } +/* Remove every idle backend connections stored in and move them + * into the purge list. If 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: