From 67df6577ffec65b4fd24b06dbe8f5f5206b0676b Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Fri, 8 Aug 2025 17:17:26 +0200 Subject: [PATCH] MEDIUM: server: close new idle conns if server in maintenance Currently, when a server is set on maintenance mode, its idle connection are scheduled for purge. However, this does not prevent currently used connection to become idle later on, even if the server is still off. Change this behavior : an idle connection is now rejected by the server if it is in maintenance. This is implemented with a new condition in srv_add_to_idle_list() which returns an error value. In this case, muxes stream detach callback will immediately free the connection. A similar change is also performed in each MUX and SSL I/O handlers and in conn_notify_mux(). An idle connection is not reinserted in its idle list if server is in maintenance, but instead it is immediately freed. --- src/connection.c | 7 +++++++ src/mux_fcgi.c | 16 +++++++++++++--- src/mux_h1.c | 16 +++++++++++++--- src/mux_h2.c | 19 +++++++++++++++---- src/mux_spop.c | 16 +++++++++++++--- src/server.c | 1 + src/ssl_sock.c | 20 +++++++++++++++----- 7 files changed, 77 insertions(+), 18 deletions(-) diff --git a/src/connection.c b/src/connection.c index be03b0d47..6dbca2c12 100644 --- a/src/connection.c +++ b/src/connection.c @@ -213,6 +213,13 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake) goto done; if (conn_in_list) { + if (srv->cur_admin & SRV_ADMF_MAINT) { + /* Do not store an idle conn if server in maintenance. */ + conn->mux->destroy(conn->ctx); + ret = -1; + goto done; + } + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c index 0b143b893..949ef19c1 100644 --- a/src/mux_fcgi.c +++ b/src/mux_fcgi.c @@ -3091,11 +3091,21 @@ struct task *fcgi_io_cb(struct task *t, void *ctx, unsigned int state) if (!ret && conn_in_list) { struct server *srv = __objt_server(conn->target); - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); - _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + if (!(srv->cur_admin & SRV_ADMF_MAINT)) { + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + } + else { + /* Do not store an idle conn if server in maintenance. */ + goto release; + } } return t; + + release: + fcgi_release(fconn); + return NULL; } /* callback called on any event by the connection handler. diff --git a/src/mux_h1.c b/src/mux_h1.c index a8a6efa93..8aeec3931 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -4328,11 +4328,21 @@ struct task *h1_io_cb(struct task *t, void *ctx, unsigned int state) if (!ret && conn_in_list) { struct server *srv = __objt_server(conn->target); - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); - _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + if (!(srv->cur_admin & SRV_ADMF_MAINT)) { + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + } + else { + /* Do not store an idle conn if server in maintenance. */ + goto release; + } } return t; + + release: + h1_release(h1c); + return NULL; } static int h1_wake(struct connection *conn) diff --git a/src/mux_h2.c b/src/mux_h2.c index 9ae45d261..bf678e26e 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -4995,14 +4995,25 @@ struct task *h2_io_cb(struct task *t, void *ctx, unsigned int state) if (!ret && conn_in_list) { struct server *srv = __objt_server(conn->target); - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); - _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + if (!(srv->cur_admin & SRV_ADMF_MAINT)) { + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + } + else { + /* Do not store an idle conn if server in maintenance. */ + goto release; + } } -leave: + leave: TRACE_LEAVE(H2_EV_H2C_WAKE); return t; + + release: + TRACE_LEAVE(H2_EV_H2C_WAKE); + h2_release(h2c); + return NULL; } /* callback called on any event by the connection handler. diff --git a/src/mux_spop.c b/src/mux_spop.c index 95000e02b..77ee3a242 100644 --- a/src/mux_spop.c +++ b/src/mux_spop.c @@ -2587,11 +2587,21 @@ static struct task *spop_io_cb(struct task *t, void *ctx, unsigned int state) if (!ret && conn_in_list) { struct server *srv = __objt_server(conn->target); - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); - _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + if (!(srv->cur_admin & SRV_ADMF_MAINT)) { + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + } + else { + /* Do not store an idle conn if server in maintenance. */ + goto release; + } } return t; + + release: + spop_release(spop_conn); + return NULL; } /* callback called on any event by the connection handler. diff --git a/src/server.c b/src/server.c index b469b7ca8..d736362f7 100644 --- a/src/server.c +++ b/src/server.c @@ -7327,6 +7327,7 @@ int srv_add_to_idle_list(struct server *srv, struct connection *conn, int is_saf */ if (!(conn->flags & CO_FL_PRIVATE) && srv && srv->pool_purge_delay > 0 && + !(srv->cur_admin & SRV_ADMF_MAINT) && ((srv->proxy->options & PR_O_REUSE_MASK) != PR_O_REUSE_NEVR) && ha_used_fds < global.tune.pool_high_count && (srv->max_idle_conns == -1 || srv->max_idle_conns > srv->curr_idle_conns) && diff --git a/src/ssl_sock.c b/src/ssl_sock.c index fc4e5a602..9342bd6d1 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -6478,12 +6478,22 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state) #endif leave: if (!ret && conn_in_list) { - struct server *srv = objt_server(conn->target); + struct server *srv = __objt_server(conn->target); - TRACE_DEVEL("adding conn back to idle list", SSL_EV_CONN_IO_CB, conn); - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); - _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + if (!(srv->cur_admin & SRV_ADMF_MAINT)) { + TRACE_DEVEL("adding conn back to idle list", SSL_EV_CONN_IO_CB, conn); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + } + else { + /* Do not store an idle conn if server in maintenance. */ + + /* Connection is idle which means MUX layer is already initialized. */ + BUG_ON(!conn->mux); + conn->mux->destroy(conn->ctx); + t = NULL; + } } TRACE_LEAVE(SSL_EV_CONN_IO_CB, conn); return t;