[MEDIUM] implement the slowstart parameter for servers

The new 'slowstart' parameter for a server accepts a value in
milliseconds which indicates after how long a server which has
just come back up will run at full speed. The speed grows
linearly from 0 to 100% during this time. The limitation applies
to two parameters :

  - maxconn: the number of connections accepted by the server
    will grow from 1 to 100% of the usual dynamic limit defined
    by (minconn,maxconn,fullconn).

  - weight: when the backend uses a dynamic weighted algorithm,
    the weight grows linearly from 1 to 100%. In this case, the
    weight is updated at every health-check. For this reason, it
    is important that the 'inter' parameter is smaller than the
    'slowstart', in order to maximize the number of steps.

The slowstart never applies when haproxy starts, otherwise it
would cause trouble to running servers. It only applies when
a server has been previously seen as failed.
This commit is contained in:
Willy Tarreau 2007-11-30 17:42:05 +01:00
parent df36614b97
commit 9909fc13f1
4 changed files with 58 additions and 8 deletions

View File

@ -42,6 +42,7 @@
#define SRV_BIND_SRC 0x0008 /* this server uses a specific source address */
#define SRV_CHECKED 0x0010 /* this server needs to be checked */
#define SRV_GOINGDOWN 0x0020 /* this server says that it's going down (404) */
#define SRV_WARMINGUP 0x0040 /* this server is warming up after a failure */
#define SRV_TPROXY_ADDR 0x0020 /* bind to this non-local address to reach this server */
#define SRV_TPROXY_CIP 0x0040 /* bind to the client's IP address to reach this server */
@ -87,6 +88,7 @@ struct server {
int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */
int rise, fall; /* time in iterations */
int inter; /* time in milliseconds */
int slowstart; /* slowstart time in seconds (ms in the conf) */
int result; /* health-check result : SRV_CHK_* */
int curfd; /* file desc used for current test, or -1 if not in test */

View File

@ -1428,6 +1428,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
newsrv->health = newsrv->rise; /* up, but will fall down at first failure */
newsrv->uweight = 1;
newsrv->maxqueue = 0;
newsrv->slowstart = 0;
cur_arg = 3;
while (*args[cur_arg]) {
@ -1484,6 +1485,11 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
newsrv->maxqueue = atol(args[cur_arg + 1]);
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "slowstart")) {
/* slowstart is stored in seconds */
newsrv->slowstart = (atol(args[cur_arg + 1]) + 999) / 1000;
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "check")) {
global.maxsock++;
do_check = 1;
@ -1540,7 +1546,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', 'maxqueue', and 'weight'.\n",
Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', 'slowstart' and 'weight'.\n",
file, linenum, newsrv->id);
return -1;
}

View File

@ -530,6 +530,25 @@ void process_chk(struct task *t, struct timeval *next)
if ((s->result & (SRV_CHK_ERROR|SRV_CHK_RUNNING)) == SRV_CHK_RUNNING) { /* good server detected */
//fprintf(stderr, "process_chk: 9\n");
if (s->state & SRV_WARMINGUP) {
if (now.tv_sec < s->last_change || now.tv_sec >= s->last_change + s->slowstart) {
s->state &= ~SRV_WARMINGUP;
if (s->proxy->lbprm.algo & BE_LB_PROP_DYN)
s->eweight = s->uweight * BE_WEIGHT_SCALE;
if (s->proxy->lbprm.update_server_eweight)
s->proxy->lbprm.update_server_eweight(s);
}
else if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) {
/* for dynamic algorithms, let's update the weight */
s->eweight = BE_WEIGHT_SCALE * (now.tv_sec - s->last_change) / s->slowstart;
s->eweight *= s->uweight;
if (s->proxy->lbprm.update_server_eweight)
s->proxy->lbprm.update_server_eweight(s);
}
/* probably that we can refill this server with a bit more connections */
check_for_pending(s);
}
/* we may have to add/remove this server from the LB group */
if ((s->state & SRV_RUNNING) && (s->proxy->options & PR_O_DISABLE404)) {
if ((s->state & SRV_GOINGDOWN) &&
@ -598,6 +617,17 @@ void process_chk(struct task *t, struct timeval *next)
s->last_change = now.tv_sec;
s->state |= SRV_RUNNING;
if (s->slowstart > 0) {
s->state |= SRV_WARMINGUP;
if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) {
/* For dynamic algorithms, start at the first step of the weight,
* without multiplying by BE_WEIGHT_SCALE.
*/
s->eweight = s->uweight;
if (s->proxy->lbprm.update_server_eweight)
s->proxy->lbprm.update_server_eweight(s);
}
}
s->proxy->lbprm.set_server_status_up(s);
/* check if we can handle some connections queued at the proxy. We

View File

@ -33,20 +33,32 @@ int init_pendconn()
/* returns the effective dynamic maxconn for a server, considering the minconn
* and the proxy's usage relative to its dynamic connections limit. It is
* expected that 0 < s->minconn <= s->maxconn when this is called.
* expected that 0 < s->minconn <= s->maxconn when this is called. If the
* server is currently warming up, the slowstart is also applied to the
* resulting value, which can be lower than minconn in this case, but never
* less than 1.
*/
unsigned int srv_dynamic_maxconn(const struct server *s)
{
unsigned int max;
if (s->proxy->beconn >= s->proxy->fullconn)
/* no fullconn or proxy is full */
return s->maxconn;
if (s->minconn == s->maxconn)
max = s->maxconn;
else if (s->minconn == s->maxconn)
/* static limit */
return s->maxconn;
max = s->maxconn;
else max = MAX(s->minconn,
s->proxy->beconn * s->maxconn / s->proxy->fullconn);
return MAX(s->minconn,
s->proxy->beconn * s->maxconn / s->proxy->fullconn);
if ((s->state & SRV_WARMINGUP) &&
now.tv_sec < s->last_change + s->slowstart &&
now.tv_sec >= s->last_change) {
unsigned int ratio;
ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
max = max * ratio / 100;
}
return max;
}