]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: server: close idle conn on server deletion
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 13 Mar 2024 10:33:50 +0000 (11:33 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 22 Mar 2024 15:59:02 +0000 (16:59 +0100)
To be able to delete a server, a number of preconditions must be
validated to ensure it is not in used anymore. Previously, if idle
connections were stored in the server, the deletion was cancelled. No
action was implemented to force idle connection closure, the only
solution was to wait for the periodic purging to be achieved.

This is an extra burden to be able to delete a server. Indeed, idle
connections are by definition inactive and can be closed prior to delete
a server. This is the exact purpose of this patch.

Idle connections removal is implemented inside "delete server" handler,
once it has been determined that the server can be freely removed. A
simple loop is run to call conn_release() over each idle connections.
Takeover is also executed before conn_release() to ensure tasks/tasklets
or any other sensible elements are not deleted from a foreign thread.

This patch should reduce the occurence of rejected "delete server"
execution, especially when connection reuse is high.

src/server.c

index e22b33806c1dca639945015394150f043a8768b2..92d3fee94cffcd642ec9d414d22b09e4e994f630 100644 (file)
@@ -5828,12 +5828,8 @@ int srv_check_for_deletion(const char *bename, const char *svname, struct proxy
        /* Second, conditions that may change over time */
        ret = 0;
 
-       /* Ensure that there is no active/idle/pending connection on the server.
-        *
-        * TODO idle connections should not prevent server deletion. A proper
-        * cleanup function should be implemented to be used here.
-        */
-       if (srv->curr_used_conns || srv->curr_idle_conns ||
+       /* Ensure that there is no active/pending connection on the server. */
+       if (srv->curr_used_conns ||
            !MT_LIST_ISEMPTY(&srv->sess_conns) ||
            !eb_is_empty(&srv->queue.head) || srv_has_streams(srv)) {
                msg = "Server still has connections attached to it, cannot remove it.";
@@ -5862,7 +5858,7 @@ static int cli_parse_delete_server(char **args, char *payload, struct appctx *ap
        struct server *prev_del;
        struct ist be_name, sv_name;
        const char *msg;
-       int ret;
+       int ret, i;
 
        if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
                return 1;
@@ -5891,6 +5887,29 @@ static int cli_parse_delete_server(char **args, char *payload, struct appctx *ap
                goto out;
        }
 
+       /* Close idle connections attached to this server. */
+       for (i = tid;;) {
+               struct list *list = &srv->per_thr[i].idle_conn_list;
+               struct connection *conn;
+
+               while (!LIST_ISEMPTY(list)) {
+                       conn = LIST_ELEM(list->n, struct connection *, idle_list);
+                       if (i != tid) {
+                               if (conn->mux && conn->mux->takeover)
+                                       conn->mux->takeover(conn, i, 1);
+                               else if (conn->xprt && conn->xprt->takeover)
+                                       conn->xprt->takeover(conn, conn->ctx, i, 1);
+                       }
+                       conn_release(conn);
+               }
+
+               if ((i = ((i + 1 == global.nbthread) ? 0 : i + 1)) == tid)
+                       break;
+       }
+
+       /* All idle connections should be removed now. */
+       BUG_ON(srv->curr_idle_conns);
+
        /* removing cannot fail anymore when we reach this:
         * publishing EVENT_HDL_SUB_SERVER_DEL
         */