MEDIUM: backend: add the "http-reuse aggressive" strategy
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.
diff --git a/src/backend.c b/src/backend.c
index 18f688d..9e8e9b6 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1046,17 +1046,43 @@
*/
}
- 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 @@
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);
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 9f7a6b5..08adccd 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -5042,6 +5042,12 @@
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 @@
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;
}
diff --git a/src/proto_http.c b/src/proto_http.c
index 02ff485..c7ed011 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -5032,6 +5032,7 @@
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 @@
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);
}