When SIGUSR1 is received, haproxy enters in soft-stop and quits when no connection remains. It can happen that the instance remains alive for a long time, depending on timeouts and traffic. This option ensures that soft-stop won't run for too long.
Example: global hard-stop-after 30s # Once in soft-stop, the instance will remain # alive for at most 30 seconds. --- doc/configuration.txt | 17 ++++++++++++++ include/types/global.h | 3 +++ src/haproxy.c | 5 +++- src/proxy.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 73a4f4b8..fb3e691d 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -536,6 +536,7 @@ The following keywords are supported in the "global" section : - external-check - gid - group + - hard-stop-after - log - log-tag - log-send-hostname @@ -703,6 +704,22 @@ gid <number> will only be able to drop these groups if started with superuser privileges. See also "group" and "uid". +hard-stop-after <time> + Defines the maximum time allowed to perform a clean soft-stop. + + Arguments : + <time> is the maximum time (by default in milliseconds) for which the + instance will remain alive when a soft-stop is received via the + SIGUSR1 signal. + + This may be used to ensure that the instance will quit even if connections + remain opened during a soft-stop (for example with long timeouts for a proxy + in tcp mode). It applies both in TCP and HTTP mode. + + Example: + global + hard-stop-after 30s + group <group name> Similar to "gid" but uses the GID of group name <group name> from /etc/group. See also "gid" and "user". diff --git a/include/types/global.h b/include/types/global.h index e14a2add..df8e2c69 100644 --- a/include/types/global.h +++ b/include/types/global.h @@ -80,6 +80,7 @@ struct global { int gid; int external_check; int nbproc; + unsigned int hard_stop_after; /* maximum time allowed to perform a soft-stop */ int maxconn, hardmaxconn; int maxsslconn; int ssl_session_max_cost; /* how many bytes an SSL session may cost */ @@ -170,6 +171,7 @@ extern const int zero; extern const int one; extern const struct linger nolinger; extern int stopping; /* non zero means stopping in progress */ +extern int killed; /* non zero means a hard-stop is triggered */ extern char hostname[MAX_HOSTNAME_LEN]; extern char localpeer[MAX_HOSTNAME_LEN]; extern struct list global_listener_queue; /* list of the temporarily limited listeners */ @@ -194,6 +196,7 @@ static inline int already_warned(unsigned int warning) return 0; } +void deinit(void); void hap_register_build_opts(const char *str, int must_free); void hap_register_post_check(int (*fct)()); void hap_register_post_deinit(void (*fct)()); diff --git a/src/haproxy.c b/src/haproxy.c index 559b4811..4f30d727 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -117,6 +117,7 @@ int relative_pid = 1; /* process id starting at 1 */ /* global options */ struct global global = { + .hard_stop_after = TICK_ETERNITY, .nbproc = 1, .req_count = 0, .logsrvs = LIST_HEAD_INIT(global.logsrvs), @@ -157,6 +158,7 @@ struct global global = { /*********************************************************************/ int stopping; /* non zero means stopping in progress */ +int killed; /* non zero means a hard-stop is triggered */ int jobs = 0; /* number of active jobs (conns, listeners, active tasks, ...) */ /* Here we store informations about the pids of the processes we may pause @@ -593,6 +595,7 @@ static void init(int argc, char **argv) */ totalconn = actconn = maxfd = listeners = stopping = 0; + killed = 0; #ifdef HAPROXY_MEMMAX @@ -1225,7 +1228,7 @@ static void deinit_stick_rules(struct list *rules) } } -static void deinit(void) +void deinit(void) { struct proxy *p = proxy, *p0; struct cap_hdr *h,*h_next; diff --git a/src/proxy.c b/src/proxy.c index 19eddcac..d158fac0 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -914,6 +914,58 @@ struct task *manage_proxy(struct task *t) } +static int proxy_parse_hard_stop_after(char **args, int section_type, struct proxy *curpx, + struct proxy *defpx, const char *file, int line, + char **err) +{ + const char *res; + + if (!*args[1]) { + memprintf(err, "'%s' expects <time> as argument.\n", args[0]); + return -1; + } + res = parse_time_err(args[1], &global.hard_stop_after, TIME_UNIT_MS); + if (res) { + memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]); + return -1; + } + return 0; +} + +struct task *hard_stop(struct task *t) +{ + struct proxy *p; + struct stream *s; + + if (killed) { + Warning("Some tasks resisted to hard-stop, exiting now.\n"); + send_log(NULL, LOG_WARNING, "Some tasks resisted to hard-stop, exiting now.\n"); + /* Do some cleanup and explicitely quit */ + deinit(); + exit(0); + } + + Warning("soft-stop running for too long, performing a hard-stop.\n"); + send_log(NULL, LOG_WARNING, "soft-stop running for too long, performing a hard-stop.\n"); + p = proxy; + while (p) { + if ((p->cap & PR_CAP_FE) && (p->feconn > 0)) { + Warning("Proxy %s hard-stopped (%d remaining conns will be closed).\n", + p->id, p->feconn); + send_log(p, LOG_WARNING, "Proxy %s hard-stopped (%d remaining conns will be closed).\n", + p->id, p->feconn); + } + p = p->next; + } + list_for_each_entry(s, &streams, list) { + stream_shutdown(s, SF_ERR_KILLED); + } + + killed = 1; + t->expire = tick_add(now_ms, MS_TO_TICKS(1000)); + return t; +} + /* * this function disables health-check servers so that the process will quickly be ignored * by load balancers. Note that if a proxy was already in the PAUSED state, then its grace @@ -923,8 +975,19 @@ void soft_stop(void) { struct proxy *p; struct peers *prs; + struct task *task; stopping = 1; + if (tick_isset(global.hard_stop_after)) { + task = task_new(); + if (task) { + task->process = hard_stop; + task_schedule(task, tick_add(now_ms, global.hard_stop_after)); + } + else { + Alert("out of memory trying to allocate the hard-stop task.\n"); + } + } p = proxy; tv_update_date(0,1); /* else, the old time before select will be used */ while (p) { @@ -1215,6 +1278,7 @@ int stream_set_backend(struct stream *s, struct proxy *be) } static struct cfg_kw_list cfg_kws = {ILH, { + { CFG_GLOBAL, "hard-stop-after", proxy_parse_hard_stop_after }, { CFG_LISTEN, "timeout", proxy_parse_timeout }, { CFG_LISTEN, "clitimeout", proxy_parse_timeout }, { CFG_LISTEN, "contimeout", proxy_parse_timeout }, -- 2.11.0