|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
Patch fpm-ondemand.v9.patch for FPM related Bug #52569Patch version 2011-07-09 12:30 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.net
Index: sapi/fpm/php-fpm.conf.in
===================================================================
--- sapi/fpm/php-fpm.conf.in (revision 312957)
+++ sapi/fpm/php-fpm.conf.in (working copy)
@@ -82,6 +82,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 ;
;;;;;;;;;;;;;;;;;;;;
@@ -150,7 +157,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.
@@ -162,17 +170,23 @@
; 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
; The number of child processes to be created when pm is set to 'static' and the
-; maximum number of child processes to be created when pm is set to 'dynamic'.
+; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
-; Note: Used when pm is set to either 'static' or 'dynamic'
+; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 5
@@ -190,6 +204,11 @@
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 3
+
+; 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
@@ -200,7 +219,7 @@
; The URI to view the FPM status page. If this value is not set, no URI will be
; recognized as a status page. It shows the following informations:
; pool - the name of the pool;
-; process manager - static or dynamic;
+; process manager - static, dynamic or ondemand;
; start time - the date and time FPM has started;
; start since - number of seconds since FPM has started;
; accepted conn - the number of request accepted by the pool;
@@ -216,7 +235,7 @@
; has started;
; max children reached - number of times, the process limit has been reached,
; when pm tries to start more children (works only for
-; pm 'dynamic');
+; pm 'dynamic' and 'ondemand');
; Value are updated in real time.
; Example output:
; pool: www
Index: sapi/fpm/fpm/fpm_request.h
===================================================================
--- sapi/fpm/fpm/fpm_request.h (revision 312961)
+++ sapi/fpm/fpm/fpm_request.h (working copy)
@@ -18,6 +18,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);
const char *fpm_request_get_stage_name(int stage);
+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 312961)
+++ 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 312961)
+++ sapi/fpm/fpm/fpm_status.c (working copy)
@@ -352,7 +352,7 @@
now_epoch = time(NULL);
spprintf(&buffer, 0, short_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 312961)
+++ 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 312961)
+++ 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 312961)
+++ sapi/fpm/fpm/fpm_conf.c (working copy)
@@ -47,12 +47,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);
@@ -66,8 +67,9 @@
struct fpm_global_config_s fpm_global_config = {
.daemonize = 1,
#ifdef HAVE_SYSLOG_H
- .syslog_facility = -1
+ .syslog_facility = -1,
#endif
+ .events_delay = 500 /* 500µs */
};
static struct fpm_worker_pool_s *current_wp = NULL;
static int ini_recursion = 0;
@@ -89,6 +91,7 @@
#endif
{ "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 }
};
@@ -116,6 +119,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) },
@@ -222,6 +226,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);
@@ -474,8 +494,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;
}
@@ -541,6 +563,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;
@@ -700,8 +723,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;
}
@@ -742,7 +765,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) {
@@ -981,6 +1020,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;
}
@@ -1346,6 +1396,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) {
@@ -1369,6 +1420,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 312961)
+++ sapi/fpm/fpm/fpm_conf.h (working copy)
@@ -32,6 +32,7 @@
#endif
int rlimit_files;
int rlimit_core;
+ long int events_delay;
};
extern struct fpm_global_config_s fpm_global_config;
@@ -56,6 +57,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;
@@ -79,7 +81,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 312961)
+++ sapi/fpm/fpm/fpm_events.c (working copy)
@@ -264,6 +264,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 +310,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 +338,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 312961)
+++ 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 312961)
+++ sapi/fpm/fpm/fpm_request.c (working copy)
@@ -295,3 +295,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 312961)
+++ sapi/fpm/fpm/fpm_children.c (working copy)
@@ -370,6 +370,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;
}
@@ -413,6 +419,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-2025 The PHP GroupAll rights reserved. |
Last updated: Sat Oct 25 02:00:01 2025 UTC |