php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login | |
Patch fpm-ondemand.v10-5.3.patch for FPM related Bug #52569Patch version 2011-07-10 17:49 UTC Return to Bug #52569 | Download this patchThis patch is obsolete Obsoleted by patches: This patch renders other patches obsolete Obsolete patches: Patch Revisions:Developer: fat@php.netIndex: sapi/fpm/php-fpm.conf.in =================================================================== --- sapi/fpm/php-fpm.conf.in (revision 312965) +++ sapi/fpm/php-fpm.conf.in (working copy) @@ -67,6 +67,13 @@ ; Default Value: system defined value ;rlimit_core = 0 +; Set the delay (in microseconds) between two consecutive events on a same +; file descriptor. +; Note: It only works for events on file descriptors, not on timeout. +; Note: Set to 0 to set no delay. +; Default: 500 +;events.delay = 0 + ;;;;;;;;;;;;;;;;;;;; ; Pool Definitions ; ;;;;;;;;;;;;;;;;;;;; @@ -135,7 +142,8 @@ ; Possible Values: ; static - a fixed number (pm.max_children) of child processes; ; dynamic - the number of child processes are set dynamically based on the -; following directives: +; following directives. With this process management, there will be +; always at least 1 children. ; pm.max_children - the maximum number of children that can ; be alive at the same time. ; pm.start_servers - the number of children created on startup. @@ -147,6 +155,12 @@ ; state (waiting to process). If the number ; of 'idle' processes is greater than this ; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. ; Note: This value is mandatory. pm = dynamic @@ -174,6 +188,11 @@ ; Note: Used only when pm is set to 'dynamic' ; Note: Mandatory when pm is set to 'dynamic' ;pm.max_spare_servers = 35 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; ; The number of requests each child process should execute before respawning. ; This can be useful to work around memory leaks in 3rd party libraries. For Index: sapi/fpm/fpm/fpm_request.h =================================================================== --- sapi/fpm/fpm/fpm_request.h (revision 312965) +++ sapi/fpm/fpm/fpm_request.h (working copy) @@ -17,6 +17,7 @@ void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *tv, int terminate_timeout, int slowlog_timeout); int fpm_request_is_idle(struct fpm_child_s *child); +int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv); enum fpm_request_stage_e { FPM_REQUEST_ACCEPTING = 1, Index: sapi/fpm/fpm/fpm_process_ctl.c =================================================================== --- sapi/fpm/fpm/fpm_process_ctl.c (revision 312965) +++ sapi/fpm/fpm/fpm_process_ctl.c (working copy) @@ -355,7 +355,24 @@ fpm_scoreboard_update(idle, active, cur_lq, -1, -1, -1, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard); } + /* this is specific to PM_STYLE_ONDEMAND */ + if (wp->config->pm == PM_STYLE_ONDEMAND) { + struct timeval last, now; + zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children", wp->config->name, active, idle); + + if (!last_idle_child) continue; + + fpm_request_last_activity(last_idle_child, &last); + fpm_clock_get(&now); + if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) { + last_idle_child->idle_kill = 1; + fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT); + } + + continue; + } + /* the rest is only used by PM_STYLE_DYNAMIC */ if (wp->config->pm != PM_STYLE_DYNAMIC) continue; @@ -472,3 +489,47 @@ } /* }}} */ +void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) /* {{{ */ +{ + struct fpm_worker_pool_s *wp = (struct fpm_worker_pool_s *)arg; + struct fpm_child_s *child; + + + if (fpm_globals.parent_pid != getpid()) { + /* prevent a event race condition when child process + * have not set up its own event loop */ + return; + } + + wp->socket_event_set = 0; + +// zlog(ZLOG_DEBUG, "[pool %s] heartbeat running_children=%d", wp->config->name, wp->running_children); + + if (wp->running_children >= wp->config->pm_max_children) { + if (!wp->warn_max_children) { + fpm_scoreboard_update(0, 0, 0, 0, 0, 1, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard); + zlog(ZLOG_WARNING, "[pool %s] server reached max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children); + wp->warn_max_children = 1; + } + + return; + } + + for (child = wp->children; child; child = child->next) { + /* if there is at least on idle child, it will handle the connection, stop here */ + if (fpm_request_is_idle(child)) { + return; + } + } + + wp->warn_max_children = 0; + fpm_children_make(wp, 1, 1, 1); + + if (fpm_globals.is_child) { + return; + } + + zlog(ZLOG_DEBUG, "[pool %s] got accept without idle child available .... I forked", wp->config->name); +} +/* }}} */ + Index: sapi/fpm/fpm/fpm_status.c =================================================================== --- sapi/fpm/fpm/fpm_status.c (revision 312965) +++ sapi/fpm/fpm/fpm_status.c (working copy) @@ -215,7 +215,7 @@ now_epoch = time(NULL); spprintf(&buffer, 0, syntax, scoreboard.pool, - scoreboard.pm == PM_STYLE_STATIC ? "static" : "dynamic", + scoreboard.pm == PM_STYLE_STATIC ? "static" : (scoreboard.pm == PM_STYLE_DYNAMIC ? "dynamic" : "ondemand"), time_buffer, now_epoch - scoreboard.start_epoch, scoreboard.requests, Index: sapi/fpm/fpm/fpm_process_ctl.h =================================================================== --- sapi/fpm/fpm/fpm_process_ctl.h (revision 312965) +++ sapi/fpm/fpm/fpm_process_ctl.h (working copy) @@ -22,6 +22,7 @@ void fpm_pctl_kill_all(int signo); void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg); void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg); +void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg); int fpm_pctl_child_exited(); int fpm_pctl_init_main(); Index: sapi/fpm/fpm/fpm.h =================================================================== --- sapi/fpm/fpm/fpm.h (revision 312965) +++ sapi/fpm/fpm/fpm.h (working copy) @@ -24,6 +24,7 @@ int max_requests; /* for this child */ int is_child; int test_successful; + long int events_delay; }; extern struct fpm_globals_s fpm_globals; Index: sapi/fpm/fpm/fpm_conf.c =================================================================== --- sapi/fpm/fpm/fpm_conf.c (revision 312965) +++ sapi/fpm/fpm/fpm_conf.c (working copy) @@ -46,12 +46,13 @@ #define STR2STR(a) (a ? a : "undefined") #define BOOL2STR(a) (a ? "yes" : "no") -#define PM2STR(a) (a == PM_STYLE_STATIC ? "static" : "dynamic") +#define PM2STR(a) (a == PM_STYLE_STATIC ? "static" : (a == PM_STYLE_DYNAMIC ? "dynamic" : "ondemand")) #define GO(field) offsetof(struct fpm_global_config_s, field) #define WPO(field) offsetof(struct fpm_worker_pool_config_s, field) static int fpm_conf_load_ini_file(char *filename TSRMLS_DC); static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset); +static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset); static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset); static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset); static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset); @@ -59,7 +60,7 @@ static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset); static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset); -struct fpm_global_config_s fpm_global_config = { .daemonize = 1 }; +struct fpm_global_config_s fpm_global_config = { .daemonize = 1, .events_delay = 500 }; static struct fpm_worker_pool_s *current_wp = NULL; static int ini_recursion = 0; static char *ini_filename = NULL; @@ -76,6 +77,7 @@ { "log_level", &fpm_conf_set_log_level, 0 }, { "rlimit_files", &fpm_conf_set_integer, GO(rlimit_files) }, { "rlimit_core", &fpm_conf_set_rlimit_core, GO(rlimit_core) }, + { "events.delay", &fpm_conf_set_long, GO(events_delay) }, { 0, 0, 0 } }; @@ -103,6 +105,7 @@ { "pm.start_servers", &fpm_conf_set_integer, WPO(pm_start_servers) }, { "pm.min_spare_servers", &fpm_conf_set_integer, WPO(pm_min_spare_servers) }, { "pm.max_spare_servers", &fpm_conf_set_integer, WPO(pm_max_spare_servers) }, + { "pm.process_idle_timeout", &fpm_conf_set_time, WPO(pm_process_idle_timeout) }, { "pm.status_path", &fpm_conf_set_string, WPO(pm_status_path) }, { "ping.path", &fpm_conf_set_string, WPO(ping_path) }, { "ping.response", &fpm_conf_set_string, WPO(ping_response) }, @@ -209,6 +212,22 @@ } /* }}} */ +static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset) /* {{{ */ +{ + char *val = Z_STRVAL_P(value); + char *p; + + for (p = val; *p; p++) { + if ( p == val && *p == '-' ) continue; + if (*p < '0' || *p > '9') { + return "is not a valid number (greater or equal than zero)"; + } + } + * (long int *) ((char *) *config + offset) = atol(val); + return NULL; +} +/* }}} */ + static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset) /* {{{ */ { char *val = Z_STRVAL_P(value); @@ -308,8 +327,10 @@ c->pm = PM_STYLE_STATIC; } else if (!strcasecmp(val, "dynamic")) { c->pm = PM_STYLE_DYNAMIC; + } else if (!strcasecmp(val, "ondemand")) { + c->pm = PM_STYLE_ONDEMAND; } else { - return "invalid process manager (static or dynamic)"; + return "invalid process manager (static, dynamic or ondemand)"; } return NULL; } @@ -375,6 +396,7 @@ memset(wp->config, 0, sizeof(struct fpm_worker_pool_config_s)); wp->config->listen_backlog = FPM_BACKLOG_DEFAULT; + wp->config->pm_process_idle_timeout = 10; /* 10s by default */ if (!fpm_worker_all_pools) { fpm_worker_all_pools = wp; @@ -534,8 +556,8 @@ return -1; } - if (wp->config->pm != PM_STYLE_STATIC && wp->config->pm != PM_STYLE_DYNAMIC) { - zlog(ZLOG_ALERT, "[pool %s] the process manager is missing (static or dynamic)", wp->config->name); + if (wp->config->pm != PM_STYLE_STATIC && wp->config->pm != PM_STYLE_DYNAMIC && wp->config->pm != PM_STYLE_ONDEMAND) { + zlog(ZLOG_ALERT, "[pool %s] the process manager is missing (static, dynamic or ondemand)", wp->config->name); return -1; } @@ -576,7 +598,23 @@ zlog(ZLOG_ALERT, "[pool %s] pm.start_servers(%d) must not be less than pm.min_spare_servers(%d) and not greater than pm.max_spare_servers(%d)", wp->config->name, config->pm_start_servers, config->pm_min_spare_servers, config->pm_max_spare_servers); return -1; } + } else if (wp->config->pm == PM_STYLE_ONDEMAND) { + struct fpm_worker_pool_config_s *config = wp->config; + if (config->pm_process_idle_timeout < 1) { + zlog(ZLOG_ALERT, "[pool %s] pm.process_idle_timeout(%ds) must be greater than 0s", wp->config->name, config->pm_process_idle_timeout); + return -1; + } + + if (config->listen_backlog < FPM_BACKLOG_DEFAULT) { + zlog(ZLOG_WARNING, "[pool %s] listen.backlog(%d) was too low for the ondemand process manager. I updated it for you to %d.", wp->config->name, config->listen_backlog, FPM_BACKLOG_DEFAULT); + config->listen_backlog = FPM_BACKLOG_DEFAULT; + } + + /* certainely useless but proper */ + config->pm_start_servers = 0; + config->pm_min_spare_servers = 0; + config->pm_max_spare_servers = 0; } if (wp->config->slowlog && *wp->config->slowlog) { @@ -800,6 +838,17 @@ fpm_evaluate_full_path(&fpm_global_config.error_log, NULL, PHP_LOCALSTATEDIR, 0); + if (fpm_global_config.events_delay < 0 ) { + zlog(ZLOG_ERROR, "events.delay must be null or positive"); + return -1; + } + + if (fpm_global_config.events_delay > 999999) { + zlog(ZLOG_ERROR, "events.delay max is 999999us (less than 1s)"); + return -1; + } + fpm_globals.events_delay = fpm_global_config.events_delay; + if (0 > fpm_stdio_open_error_log(0)) { return -1; } @@ -1157,6 +1206,7 @@ zlog(ZLOG_NOTICE, "\temergency_restart_threshold = %d", fpm_global_config.emergency_restart_threshold); zlog(ZLOG_NOTICE, "\trlimit_files = %d", fpm_global_config.rlimit_files); zlog(ZLOG_NOTICE, "\trlimit_core = %d", fpm_global_config.rlimit_core); + zlog(ZLOG_NOTICE, "\tevents.delay = %lu", fpm_global_config.events_delay); zlog(ZLOG_NOTICE, " "); for (wp = fpm_worker_all_pools; wp; wp = wp->next) { @@ -1180,6 +1230,7 @@ zlog(ZLOG_NOTICE, "\tpm.start_servers = %d", wp->config->pm_start_servers); zlog(ZLOG_NOTICE, "\tpm.min_spare_servers = %d", wp->config->pm_min_spare_servers); zlog(ZLOG_NOTICE, "\tpm.max_spare_servers = %d", wp->config->pm_max_spare_servers); + zlog(ZLOG_NOTICE, "\tpm.process_idle_timeout = %d", wp->config->pm_process_idle_timeout); zlog(ZLOG_NOTICE, "\tpm.status_path = %s", STR2STR(wp->config->pm_status_path)); zlog(ZLOG_NOTICE, "\tping.path = %s", STR2STR(wp->config->ping_path)); zlog(ZLOG_NOTICE, "\tping.response = %s", STR2STR(wp->config->ping_response)); Index: sapi/fpm/fpm/fpm_conf.h =================================================================== --- sapi/fpm/fpm/fpm_conf.h (revision 312965) +++ sapi/fpm/fpm/fpm_conf.h (working copy) @@ -27,6 +27,7 @@ char *error_log; int rlimit_files; int rlimit_core; + long int events_delay; }; extern struct fpm_global_config_s fpm_global_config; @@ -51,6 +52,7 @@ int pm_start_servers; int pm_min_spare_servers; int pm_max_spare_servers; + int pm_process_idle_timeout; char *ping_path; char *ping_response; char *access_log; @@ -74,7 +76,8 @@ enum { PM_STYLE_STATIC = 1, - PM_STYLE_DYNAMIC = 2 + PM_STYLE_DYNAMIC = 2, + PM_STYLE_ONDEMAND = 3 }; int fpm_conf_init_main(int test_conf); Index: sapi/fpm/fpm/fpm_events.c =================================================================== --- sapi/fpm/fpm/fpm_events.c (revision 312965) +++ sapi/fpm/fpm/fpm_events.c (working copy) @@ -227,6 +227,9 @@ if (wp->config->catch_workers_output && wp->config->pm_max_children > 0) { fpm_event_nfds_max += (wp->config->pm_max_children * 2); } + if (wp->config->pm == PM_STYLE_ONDEMAND) { + fpm_event_nfds_max++; + } } /* malloc the max number of necessary fds for polling */ @@ -264,6 +267,7 @@ fpm_pctl_perform_idle_server_maintenance_heartbeat(NULL, 0, NULL); zlog(ZLOG_DEBUG, "%zu bytes have been reserved in SHM", fpm_shm_get_size_allocated()); + zlog(ZLOG_DEBUG, "events.delay = %lu", fpm_globals.events_delay); zlog(ZLOG_NOTICE, "ready to handle connections"); } @@ -309,6 +313,15 @@ i = 0; q = fpm_event_queue_fd; while (q && i < fpm_event_nfds_max) { + + /* if the event has been triggered less than the defined delay, skip it */ + if (fpm_globals.events_delay > 0) { + if (timercmp(&q->ev->timeout, &now, >)) { + q->ev->index = -1; + q = q->next; + continue; + } + } fpm_event_ufds[i].fd = q->ev->fd; fpm_event_ufds[i].events = POLLIN; q->ev->index = i++; @@ -328,6 +341,15 @@ if (q->ev && q->ev->index >= 0 && q->ev->index < fpm_event_nfds_max) { if (q->ev->fd == fpm_event_ufds[q->ev->index].fd) { if (fpm_event_ufds[q->ev->index].revents & POLLIN) { + + if (fpm_globals.events_delay > 0) { + fpm_clock_get(&now); + /* set a 500µs delay before trying to retrigger the event */ + ms.tv_sec = 0; + ms.tv_usec = fpm_globals.events_delay; + timeradd(&now, &ms, &q->ev->timeout); + } + fpm_event_fire(q->ev); /* sanity check */ if (fpm_globals.parent_pid != getpid()) { Index: sapi/fpm/fpm/fpm_worker_pool.h =================================================================== --- sapi/fpm/fpm/fpm_worker_pool.h (revision 312965) +++ sapi/fpm/fpm/fpm_worker_pool.h (working copy) @@ -37,6 +37,10 @@ #endif struct fpm_scoreboard_s *scoreboard; int log_fd; + + /* for ondemand PM */ + struct fpm_event_s *ondemand_event; + int socket_event_set; }; struct fpm_worker_pool_s *fpm_worker_pool_alloc(); Index: sapi/fpm/fpm/fpm_request.c =================================================================== --- sapi/fpm/fpm/fpm_request.c (revision 312965) +++ sapi/fpm/fpm/fpm_request.c (working copy) @@ -276,3 +276,20 @@ return proc->request_stage == FPM_REQUEST_ACCEPTING; } /* }}} */ + +int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv) /* {{{ */ +{ + struct fpm_scoreboard_proc_s *proc; + + if (!tv) return -1; + + proc = fpm_scoreboard_proc_get(child->wp->scoreboard, child->scoreboard_i); + if (!proc) { + return -1; + } + + *tv = proc->tv; + + return 1; +} +/* }}} */ Index: sapi/fpm/fpm/fpm_children.c =================================================================== --- sapi/fpm/fpm/fpm_children.c (revision 312965) +++ sapi/fpm/fpm/fpm_children.c (working copy) @@ -369,6 +369,12 @@ } else { max = wp->running_children + nb_to_spawn; } + } else if (wp->config->pm == PM_STYLE_ONDEMAND) { + if (!in_event_loop) { /* starting */ + max = 0; /* do not create any child at startup */ + } else { + max = wp->running_children + nb_to_spawn; + } } else { /* PM_STYLE_STATIC */ max = wp->config->pm_max_children; } @@ -412,6 +418,22 @@ int fpm_children_create_initial(struct fpm_worker_pool_s *wp) /* {{{ */ { + if (wp->config->pm == PM_STYLE_ONDEMAND) { + wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s)); + + if (!wp->ondemand_event) { + zlog(ZLOG_ERROR, "[pool %s] unable to malloc the ondemand socket event", wp->config->name); + // FIXME handle crash + return 1; + } + + memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s)); + fpm_event_set(wp->ondemand_event, wp->listening_socket, FPM_EV_READ, fpm_pctl_on_socket_accept, wp); + wp->socket_event_set = 1; + fpm_event_add(wp->ondemand_event, 0); + + return 1; + } return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1); } /* }}} */ |
Copyright © 2001-2024 The PHP Group All rights reserved. |
Last updated: Sat Dec 21 16:01:28 2024 UTC |