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


Reply via email to