MEDIUM: startup: split sending oldpids_sig logic for standalone and mworker modes

Before refactoring the master-worker mode, in all runtime modes, when the new
process successfully parsed its configuration and bound to sockets, it sent
either SIGUSR1 or SIGTERM to the previous one in order to terminate it.

Let's keep this logic as is for the standalone mode. In addition, in standalone
mode we need to send the signal to old process before calling set_identity(),
because in set_identity() effective user or group may change. So, the order is
important here.

In case of master-worker mode after refactoring, master terminates the previous
worker by itself up to receiving "READY" status from the new one in
_send_status(). Master also sets at this moment HAPROXY_LOAD_SUCCESS env
variable and checks, if there are some other workers to terminate with
max_reloads exceeded.

So, now in master-worker mode we terminate old workers only, when the new one
has successfully done all initialization steps and has sent "READY" status to
master.
This commit is contained in:
Valentine Krasnobaeva 2024-10-03 11:16:35 +02:00 committed by Willy Tarreau
parent b73a278df4
commit 81dbc2c2e2
2 changed files with 51 additions and 12 deletions

View File

@ -2495,6 +2495,8 @@ static int _send_status(char **args, char *payload, struct appctx *appctx, void
kill(proc->pid, oldpids_sig);
}
}
setenv("HAPROXY_LOAD_SUCCESS", "1", 1);
ha_notice("Loading success.\n");
return 1;
}

View File

@ -3546,16 +3546,6 @@ int main(int argc, char **argv)
}
}
if (nb_oldpids && !master)
nb_oldpids = tell_old_pids(oldpids_sig);
/* Send a SIGTERM to workers who have a too high reloads number.
* master=1 means that fork() was done before. So, at this stage we already
* have at least one new worker with reloads=0, which is bound to sockets.
*/
if (master)
mworker_kill_max_reloads(SIGTERM);
/* Note that any error at this stage will be fatal because we will not
* be able to restart the old pids.
*/
@ -3610,8 +3600,6 @@ int main(int argc, char **argv)
global.mode &= ~MODE_VERBOSE;
global.mode |= MODE_QUIET; /* ensure that we won't say anything from now */
}
setenv("HAPROXY_LOAD_SUCCESS", "1", 1);
ha_notice("Loading success.\n");
proc_self->failedreloads = 0; /* reset the number of failure */
mworker_loop();
#if defined(USE_OPENSSL) && !defined(OPENSSL_NO_DH)
@ -3660,6 +3648,15 @@ int main(int argc, char **argv)
}
ha_free(&global.chroot);
/* In master-worker mode master sends TERM to previous workers up to
* receiving status READY
*/
if (!(global.mode & MODE_MWORKER) && nb_oldpids) {
nb_oldpids = tell_old_pids(oldpids_sig);
}
/* oldpids_sig was sent to the previous process, can change uid/gid now */
set_identity(argv[0]);
/* set_identity() above might have dropped LSTCHK_NETADM or/and
@ -3796,6 +3793,46 @@ int main(int argc, char **argv)
/* when multithreading we need to let only the thread 0 handle the signals */
haproxy_unblock_signals();
/* send "READY" message to remove status PROC_O_INIT for the newly forked worker,
* master will send TERM to the previous in _send_status()
*/
if (global.mode & MODE_MWORKER) {
struct mworker_proc *proc;
int sock_pair[2];
char *msg = NULL;
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sock_pair) == -1) {
ha_alert("[%s.main()] Cannot create socketpair to update the new worker state\n",
argv[0]);
exit(1);
}
list_for_each_entry(proc, &proc_list, list) {
if (proc->pid == -1)
break;
}
if (send_fd_uxst(proc->ipc_fd[1], sock_pair[0]) == -1) {
ha_alert("[%s.main()] Cannot transfer connection fd %d over the sockpair@%d\n",
argv[0], sock_pair[0], proc->ipc_fd[1]);
close(sock_pair[0]);
close(sock_pair[1]);
exit(1);
}
close(sock_pair[0]);
memprintf(&msg, "_send_status READY %d\n", getpid());
if (send(sock_pair[1], msg, strlen(msg), 0) != strlen(msg)) {
ha_alert("[%s.main()] Failed to send READY status to master\n", argv[0]);
exit(1);
}
close(sock_pair[1]);
ha_free(&msg);
}
#if defined(USE_SYSTEMD)
if (global.tune.options & GTUNE_USE_SYSTEMD)
sd_notifyf(0, "READY=1\nMAINPID=%lu\nSTATUS=Ready.\n", (unsigned long)getpid());