We have experimented a bit with the latest haproxies and keep-alive. We
rely on haproxy to set good maxconn values for our servers so they can
operate at full speed without becoming overloaded.

When using multiple servers in a backend, "prefer-last-server" is required
to get keep-alive working, but the sessions seem to be a bit too sticky -
haproxy perfers putting the request on a server _queue_ instead of
assigning it to a server that still has some capacity left.

So we found that the average serving time increased with keep-alive.

Suggested patch attached -- NOT TESTED PROPERLY!! --  but shows the spirit
of something...

- Finn Arne
Subject: [PATCH] prefer-last-server only when a server is not busy

If a server has filled up all its session slots, let prefer-last-server
pick a different server instead of queuing the request on a busy server.
Followups to 401 and 407 results are still queued though.

When all clients use keep-alive connections and stick around for a while,
the load could be skewed too much on servers, and we even got into queuing
while servers were idle.
---
 include/proto/queue.h | 10 ++++++++--
 src/backend.c         |  5 +++--
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/include/proto/queue.h b/include/proto/queue.h
index 7bf8137..d2a2f2a 100644
--- a/include/proto/queue.h
+++ b/include/proto/queue.h
@@ -65,13 +65,19 @@ static inline struct pendconn *pendconn_from_px(const struct proxy *px) {
 	return LIST_ELEM(px->pendconns.n, struct pendconn *, list);
 }
 
+
+/* Returns 0 if all slots are full on a server, or 1 if there are slots available. */
+static inline int server_has_room(const struct server *s) {
+	return !s->maxconn || s->cur_sess < srv_dynamic_maxconn(s);
+}
+
 /* returns 0 if nothing has to be done for server <s> regarding queued connections,
  * and non-zero otherwise. If the server is down, we only check its own queue. Suited
  * for and if/else usage.
  */
 static inline int may_dequeue_tasks(const struct server *s, const struct proxy *p) {
-	return (s && (s->nbpend || (p->nbpend && srv_is_usable(s->state, s->eweight))) &&
-		(!s->maxconn || s->cur_sess < srv_dynamic_maxconn(s)));
+	return s && (s->nbpend || (p->nbpend && srv_is_usable(s->state, s->eweight))) &&
+		server_has_room(s);
 }
 
 #endif /* _PROTO_QUEUE_H */
diff --git a/src/backend.c b/src/backend.c
index d878028..f5e76b0 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -544,9 +544,10 @@ int assign_server(struct session *s)
 
 	if (conn &&
 	    (conn->flags & CO_FL_CONNECTED) &&
-	    ((s->be->options & PR_O_PREF_LAST) || (s->txn.flags & TX_PREFER_LAST)) &&
 	    objt_server(conn->target) && __objt_server(conn->target)->proxy == s->be &&
-	    srv_is_usable(__objt_server(conn->target)->state, __objt_server(conn->target)->eweight)) {
+	    srv_is_usable(__objt_server(conn->target)->state, __objt_server(conn->target)->eweight) &&
+	    (((s->be->options & PR_O_PREF_LAST) && server_has_room(__objt_server(conn->target))) ||
+	     (s->txn.flags & TX_PREFER_LAST))) {
 		/* This session was relying on a server in a previous request
 		 * and the proxy has "option prefer-current-server" set, so
 		 * let's try to reuse the same server.
-- 
1.8.4

Reply via email to