diff --git a/include/types/server.h b/include/types/server.h index b50e1936d8..36ae7cb404 100644 --- a/include/types/server.h +++ b/include/types/server.h @@ -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 */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 07caa72170..869567ffad 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -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; } diff --git a/src/checks.c b/src/checks.c index d3aa6c8ce1..c72b55f629 100644 --- a/src/checks.c +++ b/src/checks.c @@ -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 diff --git a/src/queue.c b/src/queue.c index a4670a80c3..d282fecb1e 100644 --- a/src/queue.c +++ b/src/queue.c @@ -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; }