]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: backend: add the "http-reuse aggressive" strategy
authorWilly Tarreau <w@1wt.eu>
Wed, 5 Aug 2015 15:16:33 +0000 (17:16 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 6 Aug 2015 14:29:01 +0000 (16:29 +0200)
This strategy is less extreme than "always", it only dispatches first
requests to validated reused connections, and moves a connection from
the idle list to the safe list once it has seen a second request, thus
proving that it could be reused.

src/backend.c
src/cfgparse.c
src/proto_http.c

index 18f688d1ebab838df1b1c0b342afcef98ae4cb56..9e8e9b6968749a2ae1e94548f7f7bad2cff63854 100644 (file)
@@ -1046,17 +1046,43 @@ int connect_server(struct stream *s)
                         */
                }
 
-               if (((s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_ALWS ||
-                    ((s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_SAFE && s->txn && (s->txn->flags & TX_NOT_FIRST)))
-                   && !LIST_ISEMPTY(&srv->idle_conns)) {
-                       /* We're going to have to pick the first connection
-                        * from this pool and use it for our purposes. We may
-                        * have to get rid of the current idle connection, so
-                        * for this we try to swap it with the other owner's.
-                        * That way it may remain alive for others to pick.
-                        */
+               /* Below we pick connections from the safe or idle lists based
+                * on the strategy, the fact that this is a first or second
+                * (retryable) request, with the indicated priority (1 or 2) :
+                *
+                *          SAFE                 AGGR                ALWS
+                *
+                *      +-----+-----+        +-----+-----+       +-----+-----+
+                *   req| 1st | 2nd |     req| 1st | 2nd |    req| 1st | 2nd |
+                *  ----+-----+-----+    ----+-----+-----+   ----+-----+-----+
+                *  safe|  -  |  2  |    safe|  1  |  2  |   safe|  1  |  2  |
+                *  ----+-----+-----+    ----+-----+-----+   ----+-----+-----+
+                *  idle|  -  |  1  |    idle|  -  |  1  |   idle|  2  |  1  |
+                *  ----+-----+-----+    ----+-----+-----+   ----+-----+-----+
+                */
+
+               if (!LIST_ISEMPTY(&srv->idle_conns) &&
+                   ((s->be->options & PR_O_REUSE_MASK) != PR_O_REUSE_NEVR &&
+                    s->txn && (s->txn->flags & TX_NOT_FIRST))) {
+                       srv_conn = LIST_ELEM(srv->idle_conns.n, struct connection *, list);
+               }
+               else if (!LIST_ISEMPTY(&srv->safe_conns) &&
+                        ((s->txn && (s->txn->flags & TX_NOT_FIRST)) ||
+                         (s->be->options & PR_O_REUSE_MASK) >= PR_O_REUSE_AGGR)) {
+                       srv_conn = LIST_ELEM(srv->safe_conns.n, struct connection *, list);
+               }
+               else if (!LIST_ISEMPTY(&srv->idle_conns) &&
+                        (s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_ALWS) {
                        srv_conn = LIST_ELEM(srv->idle_conns.n, struct connection *, list);
+               }
 
+               /* If we've picked a connection from the pool, we now have to
+                * detach it. We may have to get rid of the previous idle
+                * connection we had, so for this we try to swap it with the
+                * other owner's. That way it may remain alive for others to
+                * pick.
+                */
+               if (srv_conn) {
                        LIST_DEL(&srv_conn->list);
                        LIST_INIT(&srv_conn->list);
 
@@ -1069,8 +1095,8 @@ int connect_server(struct stream *s)
                        reuse = 1;
                }
 
+               /* we may have to release our connection if we couldn't swap it */
                if (old_conn && !old_conn->owner) {
-                       /* we couldn't swap our connection, let's release it */
                        LIST_DEL(&old_conn->list);
                        conn_force_close(old_conn);
                        conn_free(old_conn);
index 9f7a6b53b3806ab3d62a1850af5f1e8264e71ddf..08adccdc124658bf6217611ea3e1005bba7fa10c 100644 (file)
@@ -5042,6 +5042,12 @@ stats_error_parsing:
                        if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
                                goto out;
                }
+               else if (strcmp(args[1], "aggressive") == 0) {
+                       curproxy->options &= ~PR_O_REUSE_MASK;
+                       curproxy->options |= PR_O_REUSE_AGGR;
+                       if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
+                               goto out;
+               }
                else if (strcmp(args[1], "always") == 0) {
                        /* enable a graceful server shutdown on an HTTP 404 response */
                        curproxy->options &= ~PR_O_REUSE_MASK;
@@ -5050,7 +5056,7 @@ stats_error_parsing:
                                goto out;
                }
                else {
-                       Alert("parsing [%s:%d] : '%s' only supports 'never', 'safe', 'always'.\n", file, linenum, args[0]);
+                       Alert("parsing [%s:%d] : '%s' only supports 'never', 'safe', 'aggressive', 'always'.\n", file, linenum, args[0]);
                        err_code |= ERR_ALERT | ERR_FATAL;
                        goto out;
                }
index 02ff485d4451a4ca570d15709ec040dad0311a5d..c7ed0115967cfc42764d7b9042b3cdd82ee8bd48 100644 (file)
@@ -5032,6 +5032,7 @@ void http_end_txn_clean_session(struct stream *s)
        struct proxy *fe = strm_fe(s);
        struct connection *srv_conn;
        struct server *srv;
+       unsigned int prev_flags = s->txn->flags;
 
        /* FIXME: We need a more portable way of releasing a backend's and a
         * server's connections. We need a safer way to reinitialize buffer
@@ -5194,6 +5195,12 @@ void http_end_txn_clean_session(struct stream *s)
                else if ((srv_conn->flags & CO_FL_PRIVATE) ||
                         ((s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_NEVR))
                        si_idle_conn(&s->si[1], &srv->priv_conns);
+               else if (prev_flags & TX_NOT_FIRST)
+                       /* note: we check the request, not the connection, but
+                        * this is valid for strategies SAFE and AGGR, and in
+                        * case of ALWS, we don't care anyway.
+                        */
+                       si_idle_conn(&s->si[1], &srv->safe_conns);
                else
                        si_idle_conn(&s->si[1], &srv->idle_conns);
        }