[MEDIUM] add support for "maxqueue" to limit server queue overload

This patch adds the "maxqueue" parameter to the server. This allows new
sessions to be immediately rebalanced when the server's queue is filled.
It's useful when session stickiness is just a performance boost (even a
huge one) but not a requirement.

This should only be used if session affinity isn't a hard functional
requirement but provides performance boost by keeping server-local
caches hot and compact).

Absence of 'maxqueue' option means unlimited queue. When queue gets filled
up to 'maxqueue' client session is moved from server-local queue to a global
one.
This commit is contained in:
Elijah Epifanov 2007-10-25 20:15:38 +02:00 committed by Willy Tarreau
parent 91092e5739
commit acafc5f88c
6 changed files with 76 additions and 27 deletions

View File

@ -1364,6 +1364,21 @@ simultaneous sessions when the proxy instance will reach 10000 sessions, and
will receive only 10 simultaneous sessions when the proxy will be under 1000
sessions.
It is possible to limit server queue length in order to rebalance excess
sessions between less busy application servers IF session affinity isn't
hard functional requirement (for example it just gives huge performance boost
by keeping server-local caches hot and compact). 'maxqueue' option sets a
queue limit on a server, as in example below:
... (just the same as in example above)
server pentium3-800 192.168.1.1:80 cookie s1 weight 8 minconn 10 maxconn 100 check maxqueue 50
server opteron-2.0G 192.168.1.2:80 cookie s2 weight 20 minconn 30 maxconn 300 check maxqueue 200
server opteron-2.4G 192.168.1.3:80 cookie s3 weight 24 minconn 30 maxconn 300 check
Absence of 'maxqueue' option means unlimited queue. When queue gets filled
up to 'maxqueue' client session is moved from server-local queue to a global
one.
Notes :
-------
- The requests will not stay indefinitely in the queue, they follow the

View File

@ -1391,6 +1391,23 @@ connexions simultan
recevra seulement 10 connexions simultanées tant que le proxy sera sous les 1000
sessions.
Il est possible de limiter la taille de la file d'attente dans le but de
redistribuer les connexions destinées à un serveur en particulier qui sont trop
loin pour avoir une chance d'être servies en un temps raisonnable. Ceci n'est
acceptable que dans le cas où l'affinité entre le client et le serveur n'est
pas obligatoire, mais motivée uniquement par des raisons de performances, par
exemple, par l'utilisation d'un cache local au serveur. L'option 'maxqueue'
permet de préciser la limite par serveur, tel que dans l'exemple ci-dessous :
... (même exemple que précédemment)
server pentium3-800 192.168.1.1:80 cookie s1 weight 8 minconn 10 maxconn 100 check maxqueue 50
server opteron-2.0G 192.168.1.2:80 cookie s2 weight 20 minconn 30 maxconn 300 check maxqueue 200
server opteron-2.4G 192.168.1.3:80 cookie s3 weight 24 minconn 30 maxconn 300 check
En l'absence du paramètre 'maxqueue', la file d'un serveur n'a pas de limite
définie. Dans le cas contraire, lorsque la file atteint la limite fixée par
'maxqueue', les clients sont déplacés vers la file globale.
Notes :
-------
- la requête ne restera pas indéfiniment en file d'attente, elle est

View File

@ -64,6 +64,7 @@ struct server {
int cur_sess, cur_sess_max; /* number of currently active sessions (including syn_sent) */
unsigned maxconn, minconn; /* max # of active sessions (0 = unlimited), min# for dynamic limit. */
int nbpend, nbpend_max; /* number of pending connections */
int maxqueue; /* maximum number of pending connections allowed */
struct list pendconns; /* pending connections */
struct task *queue_mgt; /* the task associated to the queue processing */

View File

@ -294,19 +294,25 @@ int assign_server_and_queue(struct session *s)
return SRV_STATUS_INTERNAL;
if (s->flags & SN_ASSIGNED) {
/* a server does not need to be assigned, perhaps because we're in
* direct mode, or in dispatch or transparent modes where the server
* is not needed.
*/
if (s->srv &&
s->srv->maxconn && s->srv->cur_sess >= srv_dynamic_maxconn(s->srv)) {
p = pendconn_add(s);
if (p)
return SRV_STATUS_QUEUED;
else
return SRV_STATUS_FULL;
if (s->srv && s->srv->maxqueue > 0 && s->srv->nbpend >= s->srv->maxqueue) {
s->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
s->srv = NULL;
http_flush_cookie_flags(&s->txn);
} else {
/* a server does not need to be assigned, perhaps because we're in
* direct mode, or in dispatch or transparent modes where the server
* is not needed.
*/
if (s->srv &&
s->srv->maxconn && s->srv->cur_sess >= srv_dynamic_maxconn(s->srv)) {
p = pendconn_add(s);
if (p)
return SRV_STATUS_QUEUED;
else
return SRV_STATUS_FULL;
}
return SRV_STATUS_OK;
}
return SRV_STATUS_OK;
}
/* a server needs to be assigned */

View File

@ -1413,6 +1413,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
newsrv->fall = DEF_FALLTIME;
newsrv->health = newsrv->rise; /* up, but will fall down at first failure */
newsrv->uweight = 1;
newsrv->maxqueue = 0;
cur_arg = 3;
while (*args[cur_arg]) {
@ -1465,6 +1466,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
newsrv->maxconn = atol(args[cur_arg + 1]);
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "maxqueue")) {
newsrv->maxqueue = atol(args[cur_arg + 1]);
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "check")) {
global.maxsock++;
do_check = 1;
@ -1521,7 +1526,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
}
#endif
else {
Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn' and 'weight'.\n",
Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', and 'weight'.\n",
file, linenum, newsrv->id);
return -1;
}

View File

@ -372,7 +372,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
"ereq,econ,eresp,"
"wretr,wredis,"
"status,weight,act,bck,"
"chkfail,chkdown,lastchg,downtime,"
"chkfail,chkdown,lastchg,downtime,qlimit,"
"\n");
}
if (buffer_write_chunk(rep, &msg) != 0)
@ -582,20 +582,20 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
if (flags & STAT_FMT_HTML) {
/* print a new table */
chunk_printf(&msg, sizeof(trash),
"<table cols=\"23\" class=\"tbl\" width=\"100%%\">\n"
"<table cols=\"24\" class=\"tbl\" width=\"100%%\">\n"
"<tr align=\"center\" class=\"titre\">"
"<th colspan=2 class=\"pxname\">%s</th>"
"<th colspan=21 class=\"empty\"></th>"
"</tr>\n"
"<tr align=\"center\" class=\"titre\">"
"<th rowspan=2></th>"
"<th colspan=2>Queue</th><th colspan=4>Sessions</th>"
"<th colspan=3>Queue</th><th colspan=4>Sessions</th>"
"<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
"<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
"<th colspan=7>Server</th>"
"</tr>\n"
"<tr align=\"center\" class=\"titre\">"
"<th>Cur</th><th>Max</th><th>Cur</th><th>Max</th>"
"<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
"<th>Limit</th><th>Cumul</th><th>In</th><th>Out</th>"
"<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
"<th>Resp</th><th>Retr</th><th>Redis</th>"
@ -617,7 +617,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
if (flags & STAT_FMT_HTML) {
chunk_printf(&msg, sizeof(trash),
/* name, queue */
"<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=2></td>"
"<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=3></td>"
/* sessions : current, max, limit, cumul */
"<td align=right>%d</td><td align=right>%d</td>"
"<td align=right>%d</td><td align=right>%d</td>"
@ -657,7 +657,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
/* server status : reflect frontend status */
"%s,"
/* rest of server: nothing */
",,,,,,,"
",,,,,,,,"
"\n",
px->id,
px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
@ -709,8 +709,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
chunk_printf(&msg, sizeof(trash),
/* name */
"<tr align=\"center\" class=\"%s%d\"><td>%s</td>"
/* queue : current, max */
"<td align=right>%d</td><td align=right>%d</td>"
/* queue : current, max, limit */
"<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td>"
/* sessions : current, max, limit, cumul */
"<td align=right>%d</td><td align=right>%d</td>"
"<td align=right>%s</td><td align=right>%d</td>"
@ -725,8 +725,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
"",
(sv->state & SRV_BACKUP) ? "backup" : "active",
sv_state, sv->id,
sv->nbpend, sv->nbpend_max,
sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess,
sv->nbpend, sv->nbpend_max, LIM2A0(sv->maxqueue, "-"),
sv->cur_sess, sv->cur_sess_max, LIM2A1(sv->maxconn, "-"), sv->cum_sess,
sv->bytes_in, sv->bytes_out,
sv->failed_secu,
sv->failed_conns, sv->failed_resp,
@ -809,12 +809,17 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
/* check failures: unique, fatal; last change, total downtime */
if (sv->state & SRV_CHECKED)
chunk_printf(&msg, sizeof(trash),
"%d,%d,%d,%d,\n",
"%d,%d,%d,%d,",
sv->failed_checks, sv->down_trans,
now.tv_sec - sv->last_change, srv_downtime(sv));
else
chunk_printf(&msg, sizeof(trash),
",,,,\n");
",,,,");
/* queue limit and EOL */
chunk_printf(&msg, sizeof(trash),
"%s,\n",
LIM2A0(sv->maxqueue, ""));
}
if (buffer_write_chunk(rep, &msg) != 0)
return 0;
@ -845,7 +850,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
/* name */
"<tr align=center class=\"backend\"><td>Backend</td>"
/* queue : current, max */
"<td align=right>%d</td><td align=right>%d</td>"
"<td align=right>%d</td><td align=right>%d</td><td></td>"
/* sessions : current, max, limit, cumul. */
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
/* bytes : in, out */
@ -903,7 +908,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
"%d,%d,%d,"
/* rest of backend: nothing, down transformations,
* last change, total downtime. */
",%d,%d,%d,"
",%d,%d,%d,,"
"\n",
px->id,
px->nbpend /* or px->totpend ? */, px->nbpend_max,