diff --git a/doc/configuration.txt b/doc/configuration.txt index f2954a846..31aac447a 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -571,6 +571,7 @@ The following keywords are supported in the "global" section : - log-tag - log-send-hostname - lua-load + - mworker-max-reloads - nbproc - nbthread - node @@ -966,6 +967,13 @@ master-worker [no-exit-on-failure] See also "-W" in the management guide. +mworker-max-reloads + In master-worker mode, this option limits the number of time a worker can + survive to a reload. If the worker did not left after a reload, once its + number of reloads is greater than this number, the worker will receive a + SIGTERM. This option helps to keep under control the number of workers. + See also "show proc" in the Management Guide. + nbproc Creates processes when going daemon. This requires the "daemon" mode. By default, only one process is created, which is the recommended mode diff --git a/include/proto/mworker.h b/include/proto/mworker.h index bf8317f48..86f09049f 100644 --- a/include/proto/mworker.h +++ b/include/proto/mworker.h @@ -35,5 +35,6 @@ int mworker_child_nb(); int mworker_ext_launch_all(); +void mworker_kill_max_reloads(int sig); #endif /* PROTO_MWORKER_H_ */ diff --git a/src/haproxy.c b/src/haproxy.c index 8ab0c2e6f..4c371254e 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2780,6 +2780,10 @@ int main(int argc, char **argv) if (nb_oldpids && !(global.mode & MODE_MWORKER_WAIT)) nb_oldpids = tell_old_pids(oldpids_sig); + /* send a SIGTERM to workers who have a too high reloads number */ + if ((global.mode & MODE_MWORKER) && !(global.mode & MODE_MWORKER_WAIT)) + mworker_kill_max_reloads(SIGTERM); + if ((getenv("HAPROXY_MWORKER_REEXEC") == NULL)) { nb_oldpids = 0; free(oldpids); diff --git a/src/mworker.c b/src/mworker.c index abc67bd8b..8df748d3f 100644 --- a/src/mworker.c +++ b/src/mworker.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -41,6 +42,7 @@ #endif static int exitcode = -1; +static int max_reloads = -1; /* number max of reloads a worker can have until they are killed */ /* ----- children processes handling ----- */ @@ -59,6 +61,16 @@ static void mworker_kill(int sig) } } +void mworker_kill_max_reloads(int sig) +{ + struct mworker_proc *child; + + list_for_each_entry(child, &proc_list, list) { + if (max_reloads != -1 && (child->options & PROC_O_TYPE_WORKER) && + (child->pid > 0) && (child->reloads > max_reloads)) + kill(child->pid, sig); + } +} /* return 1 if a pid is a current child otherwise 0 */ int mworker_current_child(int pid) @@ -515,6 +527,41 @@ static int cli_parse_reload(char **args, char *payload, struct appctx *appctx, v } +static int mworker_parse_global_max_reloads(char **args, int section_type, struct proxy *curpx, + struct proxy *defpx, const char *file, int linenum, char **err) +{ + + int err_code = 0; + + if (alertif_too_many_args(1, file, linenum, args, &err_code)) + goto out; + + if (*(args[1]) == 0) { + memprintf(err, "%sparsing [%s:%d] : '%s' expects an integer argument.\n", *err, file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + max_reloads = atol(args[1]); + if (max_reloads < 0) { + memprintf(err, "%sparsing [%s:%d] '%s' : invalid value %d, must be >= 0", *err, file, linenum, args[0], max_reloads); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + +out: + return err_code; +} + + +static struct cfg_kw_list mworker_kws = {{ }, { + { CFG_GLOBAL, "mworker-max-reloads", mworker_parse_global_max_reloads }, + { 0, NULL, NULL }, +}}; + +INITCALL1(STG_REGISTER, cfg_register_keywords, &mworker_kws); + + /* register cli keywords */ static struct cli_kw_list cli_kws = {{ },{ { { "@", NULL }, "@ : send a command to the process", NULL, cli_io_handler_show_proc, NULL, NULL, ACCESS_MASTER_ONLY},