Skip to content

Add FPM early bootstrapping mode #6772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions sapi/fpm/fpm/fpm_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ typedef struct _php_cgi_globals_struct {
HashTable user_config_cache;
char *error_header;
char *fpm_config;
char *fpm_bootstrap;
} php_cgi_globals_struct;

/* {{{ user_config_cache
Expand Down Expand Up @@ -1392,6 +1393,7 @@ PHP_INI_BEGIN()
STD_PHP_INI_BOOLEAN("fastcgi.logging", "1", PHP_INI_SYSTEM, OnUpdateBool, fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
STD_PHP_INI_ENTRY("fastcgi.error_header", NULL, PHP_INI_SYSTEM, OnUpdateString, error_header, php_cgi_globals_struct, php_cgi_globals)
STD_PHP_INI_ENTRY("fpm.config", NULL, PHP_INI_SYSTEM, OnUpdateString, fpm_config, php_cgi_globals_struct, php_cgi_globals)
STD_PHP_INI_ENTRY("fpm.bootstrap_file", NULL, PHP_INI_SYSTEM, OnUpdateString, fpm_bootstrap, php_cgi_globals_struct, php_cgi_globals)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All FPM configuration should go to the FPM config and it should be pool specific in this case. This will be more consistent with other settings like status path for example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about early bootstrapping mode for Apache module?

PHP_INI_END()

/* {{{ php_cgi_globals_ctor */
Expand All @@ -1407,6 +1409,7 @@ static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals)
zend_hash_init(&php_cgi_globals->user_config_cache, 0, NULL, user_config_cache_entry_dtor, 1);
php_cgi_globals->error_header = NULL;
php_cgi_globals->fpm_config = NULL;
php_cgi_globals->fpm_bootstrap = NULL;
}
/* }}} */

Expand Down Expand Up @@ -1513,6 +1516,8 @@ int main(int argc, char *argv[])
int cgi = 0, c, use_extended_info = 0;
zend_file_handle file_handle;

zend_file_handle bootstrap_file;

/* temporary locals */
int orig_optind = php_optind;
char *orig_optarg = php_optarg;
Expand Down Expand Up @@ -1836,25 +1841,50 @@ consult the installation file that came with this distribution, or visit \n\

/* library is already initialized, now init our request */
request = fpm_init_request(fcgi_fd);
int fpm_bootstrapped = CGIG(fpm_bootstrap) && CGIG(fpm_bootstrap)[0];

zend_first_try {
while (EXPECTED(fcgi_accept_request(request) >= 0)) {
while (1) {
// moving this before init_request_info will break with dtrace
// support in php_request_startup(), can we remove?
if (UNEXPECTED(fpm_bootstrapped && php_request_startup() == FAILURE)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: you should probably move this to the condition below (no need to check fpm_bootstrapped twice)

fcgi_finish_request(request, 1);
SG(server_context) = NULL;
php_module_shutdown();
return FPM_EXIT_SOFTWARE;
}

if (fpm_bootstrapped) {
zend_stream_init_filename(&bootstrap_file, CGIG(fpm_bootstrap));

if (zend_execute_scripts(ZEND_REQUIRE, NULL, 1, &bootstrap_file) == FAILURE) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here is the main missing piece that I see from my quick review. You are starting potentially longer work without changing the request_stage (updating scoreboard). That might result into various problems with dynamic and ondemand process managers because FPM master will think that there is no work going on and consider the process as idle. It means it might not scale properly which might be problematic. It will probably cause a shorter clean up for ondemand as it bases last idle time on the end of request currently. I think it would make sense to introduce a new request stage but it probably requires a bit more thinking and carefully considering all edge cases.

Another thing to consider are reloads that usually wait for children to finish the request so it can nicely restart. The question is what should happen for bootstrap and if there's any point to wait.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could allow this option only for static pools, it does make some sense for dynamic, but not for ondemand i guess

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except I guess not many users really use static so it could end up being a bit useless feature :) It's a bit stupid PM so I'd really like not to give it a preference. The dynamic is the default so think this one will be the most used one and from the bug reports it looks that people are using ondemand as well. The dynamic is doable so it really needs to be done.

The ondemand might be a bit trick but it's kind of related in some way to the reloads which should still be done cleanly. We might need to figure out how to do a proper clean up because otherwise it might end without calling php_request_shutdown, hanging scoreboard proc (needed for dynamic) and some other things.

I think the solution might be introducing event loop (epoll) in the worker which could hook up the signal handling (checking pipe where the signal handler writes) and then do clean shutdown if SIGTERM received for example. It would help with other things a well - see for example discussion in #4101 . I think this might be a prerequisite for this feature.

fcgi_finish_request(request, 1);
SG(server_context) = NULL;
php_module_shutdown();
return FPM_EXIT_SOFTWARE;
}
}

if (UNEXPECTED(fcgi_accept_request(request) < 0)) {
break;
}

char *primary_script = NULL;
request_body_fd = -1;
SG(server_context) = (void *) request;

init_request_info();

fpm_request_info();

/* request startup only after we've done all we can to
* get path_translated */
if (UNEXPECTED(php_request_startup() == FAILURE)) {
if (UNEXPECTED(fpm_bootstrapped == 0 && php_request_startup() == FAILURE)) {
fcgi_finish_request(request, 1);
SG(server_context) = NULL;
php_module_shutdown();
return FPM_EXIT_SOFTWARE;
}


/* check if request_method has been sent.
* if not, it's certainly not an HTTP over fcgi request */
if (UNEXPECTED(!SG(request_info).request_method)) {
Expand Down