Skip to content

Commit 64e8759

Browse files
committed
php-fpm: graceful restart without blocking/losing requests
## Issue: When php-fpm gets "graceful" reloaded it will stop accepting requests while finishing reqests it already received. If those requests to finish are of a long running kind like big uploads over a slow connection, this will block new requests to be handled for an unacceptable long time. ### Current behavior ``` | v FPM (OLD) running | v $ sudo kill -USR2 $(cat /run/php-fpm/php-fpm.pid) | v OLD sends SIGQUIT to children v OLD stops handling requests | v OLD keeps running until all children complete or time out | v OLD exec()s v FPM (NEW) starts & re-uses listening socket | v NEW handles incoming requests from now on ``` ## Proposed Solution: In the case of a reloading request, we immediately fork and exec a new fpm instance to seamlessly handle new requests, while the old instance keeps finishing those requests already accepted. ### New behavior ``` | v FPM (OLD) running | v $ sudo kill -USR2 $(cat /run/php-fpm/php-fpm.pid) | v OLD sends SIGQUIT to children v OLD fork()s & exec()s |\ | \ | v FPM (NEW) starts & re-uses listening socket | v NEW handles incoming requests from now on | | v | OLD keeps running until all children complete or time out v | OLD gone - | / / v NEW still going | ``` We've been running this patch since PHP 5.6 (early 2017).
1 parent 38bca53 commit 64e8759

File tree

3 files changed

+33
-4
lines changed

3 files changed

+33
-4
lines changed

UPGRADING

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,12 @@ PHP 8.0 UPGRADE NOTES
588588
if it is defined. Otherwise, DOCUMENT_ROOT will be used as before. This
589589
improves support for Apache mod_userdir and mod_alias.
590590

591+
- FPM:
592+
. Graceful restart (SIGUSR2) behavior changed to immediately fork and exec a
593+
new FPM instance while the old instance finishes the legacy of requests.
594+
IMPORTANT NOTE: Existing start-stop scripts and/or daemon units may have to
595+
be adopted to this behavior (two instances of FPM active while reloading).
596+
591597
========================================
592598
4. Deprecated Functionality
593599
========================================

sapi/fpm/fpm/fpm_process_ctl.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ static void fpm_pctl_exec() /* {{{ */
8282
zlog(ZLOG_WARNING, "concurrent reloads may be unstable");
8383
}
8484

85+
switch (fork()) {
86+
case 0:
87+
break;
88+
case -1:
89+
zlog(ZLOG_SYSERROR, "failed to reload: fork() failed");
90+
/* no break */
91+
default:
92+
fpm_global_config.pid_file = NULL;
93+
return;
94+
}
8595
zlog(ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\""
8696
"%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
8797
"%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
@@ -110,9 +120,8 @@ static void fpm_pctl_action_last() /* {{{ */
110120
{
111121
switch (fpm_state) {
112122
case FPM_PCTL_STATE_RELOADING:
113-
fpm_pctl_exec();
114-
break;
115-
123+
zlog(ZLOG_NOTICE, "exiting after reload");
124+
exit(FPM_EXIT_OK);
116125
case FPM_PCTL_STATE_FINISHING:
117126
case FPM_PCTL_STATE_TERMINATING:
118127
fpm_pctl_exit();
@@ -199,6 +208,10 @@ static void fpm_pctl_action_next() /* {{{ */
199208
fpm_pctl_kill_all(sig);
200209
fpm_signal_sent = sig;
201210
fpm_pctl_timeout_set(timeout);
211+
212+
if (fpm_signal_sent == SIGQUIT && fpm_state == FPM_PCTL_STATE_RELOADING) {
213+
fpm_pctl_exec();
214+
}
202215
}
203216
/* }}} */
204217

sapi/fpm/fpm/fpm_sockets.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,17 @@ static void fpm_sockets_cleanup(int which, void *arg) /* {{{ */
7474

7575
if (which == FPM_CLEANUP_PARENT_EXIT_MAIN) {
7676
if (ls->type == FPM_AF_UNIX) {
77-
unlink(ls->key);
77+
struct sockaddr_un sa_un;
78+
79+
memset(&sa_un, 0, sizeof(sa_un));
80+
strlcpy(sa_un.sun_path, ls->key, sizeof(sa_un.sun_path));
81+
sa_un.sun_family = AF_UNIX;
82+
83+
if (fpm_socket_unix_test_connect(&sa_un, sizeof(sa_un)) == 0) {
84+
zlog(ZLOG_WARNING, "Keeping unix socket, another FPM instance seems to already listen on %s", ls->key);
85+
} else {
86+
unlink(ls->key);
87+
}
7888
}
7989
}
8090
free(ls->key);

0 commit comments

Comments
 (0)