]> granicus.if.org Git - apache/commitdiff
mpm_event(opt), mpm_worker, mpm_prefork: follow up to r1635521, r1640161.
authorYann Ylavic <ylavic@apache.org>
Thu, 20 Nov 2014 16:26:04 +0000 (16:26 +0000)
committerYann Ylavic <ylavic@apache.org>
Thu, 20 Nov 2014 16:26:04 +0000 (16:26 +0000)
Retain num_buckets and max_buckets accross restarts so that we can determine
whether new buckets were allocated and set their idle_spawn_rate at the same
level as the existing ones (max).

Also, adjust ap_daemons_limit and ap_daemons_to_start lower bounds at mpm_run()
time, once num_buckets is available for the current generation (previously done
at check_config time, hence before num_buckets is computed, and then with the
previous generation's value).

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1640763 13f79535-47bb-0310-9956-ffa450edef68

server/mpm/event/event.c
server/mpm/eventopt/eventopt.c
server/mpm/prefork/prefork.c
server/mpm/worker/worker.c

index a5e312491b7db070d741b311a25414370c323580..4635a2e5245dbcb20684aa37e7344e68172d1705 100644 (file)
@@ -350,15 +350,20 @@ typedef struct event_retained_data {
     /*
      * idle_spawn_rate is the number of children that will be spawned on the
      * next maintenance cycle if there aren't enough idle servers.  It is
-     * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
-     * without the need to spawn.
+     * maintained per listeners bucket, doubled up to MAX_SPAWN_RATE, and
+     * reset only when a cycle goes by without the need to spawn.
      */
-    int *idle_spawn_rate,
-         idle_spawn_rate_len;
+    int *idle_spawn_rate;
 #ifndef MAX_SPAWN_RATE
 #define MAX_SPAWN_RATE        (32)
 #endif
     int hold_off_on_exponential_spawning;
+    /*
+     * Current number of listeners buckets and maximum reached accross
+     * restarts (to size retained data according to dynamic num_buckets,
+     * eg. idle_spawn_rate).
+     */
+    int num_buckets, max_buckets;
 } event_retained_data;
 static event_retained_data *retained;
 
@@ -366,7 +371,6 @@ typedef struct event_child_bucket {
     ap_pod_t *pod;
     ap_listen_rec *listeners;
 } event_child_bucket;
-static int                 num_buckets; /* Number of listeners buckets */
 static event_child_bucket *all_buckets, /* All listeners buckets */
                           *my_bucket;   /* Current child bucket */
 
@@ -2425,7 +2429,7 @@ static void child_main(int child_num_arg, int child_bucket)
     apr_pool_create(&pchild, pconf);
 
     /* close unused listeners and pods */
-    for (i = 0; i < num_buckets; i++) {
+    for (i = 0; i < retained->num_buckets; i++) {
         if (i != child_bucket) {
             ap_close_listeners_ex(all_buckets[i].listeners);
             ap_mpm_podx_close(all_buckets[i].pod);
@@ -2664,14 +2668,14 @@ static void startup_children(int number_to_start)
         if (ap_scoreboard_image->parent[i].pid != 0) {
             continue;
         }
-        if (make_child(ap_server_conf, i, i % num_buckets) < 0) {
+        if (make_child(ap_server_conf, i, i % retained->num_buckets) < 0) {
             break;
         }
         --number_to_start;
     }
 }
 
-static void perform_idle_server_maintenance(int child_bucket)
+static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
 {
     int i, j;
     int idle_thread_count;
@@ -2845,8 +2849,8 @@ static void perform_idle_server_maintenance(int child_bucket)
             if (retained->hold_off_on_exponential_spawning) {
                 --retained->hold_off_on_exponential_spawning;
             }
-            else if (retained->idle_spawn_rate[child_bucket] <
-                     MAX_SPAWN_RATE / num_buckets) {
+            else if (retained->idle_spawn_rate[child_bucket]
+                     MAX_SPAWN_RATE / num_buckets) {
                 retained->idle_spawn_rate[child_bucket] *= 2;
             }
         }
@@ -2856,7 +2860,7 @@ static void perform_idle_server_maintenance(int child_bucket)
     }
 }
 
-static void server_main_loop(int remaining_children_to_start)
+static void server_main_loop(int remaining_children_to_start, int num_buckets)
 {
     ap_generation_t old_gen;
     int child_slot;
@@ -2972,15 +2976,15 @@ static void server_main_loop(int remaining_children_to_start)
         }
 
         for (i = 0; i < num_buckets; i++) {
-            perform_idle_server_maintenance(i);
+            perform_idle_server_maintenance(i, num_buckets);
         }
     }
 }
 
 static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
 {
+    int num_buckets = retained->num_buckets;
     int remaining_children_to_start;
-
     int i;
 
     ap_log_pid(pconf, ap_pid_fname);
@@ -2999,7 +3003,13 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
     restart_pending = shutdown_pending = 0;
     set_signals();
 
-    /* Don't thrash... */
+    /* Don't thrash since num_buckets depends on the
+     * system and the number of online CPU cores...
+     */
+    if (ap_daemons_limit < num_buckets)
+        ap_daemons_limit = num_buckets;
+    if (ap_daemons_to_start < num_buckets)
+        ap_daemons_to_start = num_buckets;
     if (min_spare_threads < threads_per_child * num_buckets)
         min_spare_threads = threads_per_child * num_buckets;
     if (max_spare_threads < min_spare_threads + threads_per_child * num_buckets)
@@ -3038,7 +3048,7 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
 
     mpm_state = AP_MPMQ_RUNNING;
 
-    server_main_loop(remaining_children_to_start);
+    server_main_loop(remaining_children_to_start, num_buckets);
     mpm_state = AP_MPMQ_STOPPING;
 
     if (shutdown_pending && !retained->is_graceful) {
@@ -3178,6 +3188,7 @@ static int event_open_logs(apr_pool_t * p, apr_pool_t * plog,
 {
     int startup = 0;
     int level_flags = 0;
+    int num_buckets = 0;
     ap_listen_rec **listen_buckets;
     apr_status_t rv;
     int i;
@@ -3200,9 +3211,9 @@ static int event_open_logs(apr_pool_t * p, apr_pool_t * plog,
     if (one_process) {
         num_buckets = 1;
     }
-    else if (!retained->is_graceful) { /* Preserve the number of buckets
-                                          on graceful restarts. */
-        num_buckets = 0;
+    else if (retained->is_graceful) {
+        /* Preserve the number of buckets on graceful restarts. */
+        num_buckets = retained->num_buckets;
     }
     if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
                                      &listen_buckets, &num_buckets))) {
@@ -3211,8 +3222,8 @@ static int event_open_logs(apr_pool_t * p, apr_pool_t * plog,
                      "could not duplicate listeners");
         return DONE;
     }
-    all_buckets = apr_pcalloc(pconf, num_buckets *
-                                     sizeof(event_child_bucket));
+
+    all_buckets = apr_pcalloc(pconf, num_buckets * sizeof(*all_buckets));
     for (i = 0; i < num_buckets; i++) {
         if (!one_process && /* no POD in one_process mode */
                 (rv = ap_mpm_podx_open(pconf, &all_buckets[i].pod))) {
@@ -3224,21 +3235,34 @@ static int event_open_logs(apr_pool_t * p, apr_pool_t * plog,
         all_buckets[i].listeners = listen_buckets[i];
     }
 
-    if (retained->idle_spawn_rate_len < num_buckets) {
-        int *new_ptr, new_len;
-        new_len = retained->idle_spawn_rate_len * 2;
-        if (new_len < num_buckets) {
-            new_len = num_buckets;
+    if (retained->max_buckets < num_buckets) {
+        int new_max, *new_ptr;
+        new_max = retained->max_buckets * 2;
+        if (new_max < num_buckets) {
+            new_max = num_buckets;
         }
-        new_ptr = (int *)apr_palloc(ap_pglobal, new_len * sizeof(int));
+        new_ptr = (int *)apr_palloc(ap_pglobal, new_max * sizeof(int));
         memcpy(new_ptr, retained->idle_spawn_rate,
-               retained->idle_spawn_rate_len * sizeof(int));
-        for (i = retained->idle_spawn_rate_len; i < new_len; i++) {
-            new_ptr[i] = 1;
-        }
-        retained->idle_spawn_rate_len = new_len;
+               retained->num_buckets * sizeof(int));
         retained->idle_spawn_rate = new_ptr;
+        retained->max_buckets = new_max;
+    }
+    if (retained->num_buckets < num_buckets) {
+        int rate_max = 1;
+        /* If new buckets are added, set their idle spawn rate to
+         * the highest so far, so that they get filled as quickly
+         * as the existing ones.
+         */
+        for (i = 0; i < retained->num_buckets; i++) {
+            if (rate_max < retained->idle_spawn_rate[i]) {
+                rate_max = retained->idle_spawn_rate[i];
+            }
+        }
+        for (/* up to date i */; i < num_buckets; i++) {
+            retained->idle_spawn_rate[i] = rate_max;
+        }
     }
+    retained->num_buckets = num_buckets;
 
     /* for skiplist */
     srand((unsigned int)apr_time_now());
@@ -3522,12 +3546,6 @@ static int event_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         ap_daemons_limit = server_limit;
     }
-    else if (ap_daemons_limit < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        ap_daemons_limit = num_buckets;
-    }
 
     /* ap_daemons_to_start > ap_daemons_limit checked in ap_mpm_run() */
     if (ap_daemons_to_start < 1) {
@@ -3542,12 +3560,6 @@ static int event_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         ap_daemons_to_start = 1;
     }
-    if (ap_daemons_to_start < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        ap_daemons_to_start = num_buckets;
-    }
 
     if (min_spare_threads < 1) {
         if (startup) {
@@ -3565,12 +3577,6 @@ static int event_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         min_spare_threads = 1;
     }
-    if (min_spare_threads < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        min_spare_threads = num_buckets;
-    }
 
     /* max_spare_threads < min_spare_threads + threads_per_child
      * checked in ap_mpm_run()
index dd002ac831e3274711221229f9d979f03670fa01..a5d1c7881de670aec8116ccace7745ade85457af 100644 (file)
@@ -335,15 +335,20 @@ typedef struct event_retained_data {
     /*
      * idle_spawn_rate is the number of children that will be spawned on the
      * next maintenance cycle if there aren't enough idle servers.  It is
-     * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
-     * without the need to spawn.
+     * maintained per listeners bucket, doubled up to MAX_SPAWN_RATE, and
+     * reset only when a cycle goes by without the need to spawn.
      */
-    int *idle_spawn_rate,
-         idle_spawn_rate_len;
+    int *idle_spawn_rate;
 #ifndef MAX_SPAWN_RATE
 #define MAX_SPAWN_RATE        (32)
 #endif
     int hold_off_on_exponential_spawning;
+    /*
+     * Current number of listeners buckets and maximum reached accross
+     * restarts (to size retained data according to dynamic num_buckets,
+     * eg. idle_spawn_rate).
+     */
+    int num_buckets, max_buckets;
 } event_retained_data;
 static event_retained_data *retained;
 
@@ -351,7 +356,6 @@ typedef struct event_child_bucket {
     ap_pod_t *pod;
     ap_listen_rec *listeners;
 } event_child_bucket;
-static int                 num_buckets; /* Number of listeners buckets */
 static event_child_bucket *all_buckets, /* All listeners buckets */
                           *my_bucket;   /* Current child bucket */
 
@@ -2245,7 +2249,7 @@ static void child_main(int child_num_arg, int child_bucket)
     apr_pool_create(&pchild, pconf);
 
     /* close unused listeners and pods */
-    for (i = 0; i < num_buckets; i++) {
+    for (i = 0; i < retained->num_buckets; i++) {
         if (i != child_bucket) {
             ap_close_listeners_ex(all_buckets[i].listeners);
             ap_mpm_podx_close(all_buckets[i].pod);
@@ -2484,14 +2488,14 @@ static void startup_children(int number_to_start)
         if (ap_scoreboard_image->parent[i].pid != 0) {
             continue;
         }
-        if (make_child(ap_server_conf, i, i % num_buckets) < 0) {
+        if (make_child(ap_server_conf, i, i % retained->num_buckets) < 0) {
             break;
         }
         --number_to_start;
     }
 }
 
-static void perform_idle_server_maintenance(int child_bucket)
+static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
 {
     int i, j;
     int idle_thread_count;
@@ -2675,7 +2679,7 @@ static void perform_idle_server_maintenance(int child_bucket)
     }
 }
 
-static void server_main_loop(int remaining_children_to_start)
+static void server_main_loop(int remaining_children_to_start, int num_buckets)
 {
     ap_generation_t old_gen;
     int child_slot;
@@ -2791,15 +2795,15 @@ static void server_main_loop(int remaining_children_to_start)
         }
 
         for (i = 0; i < num_buckets; i++) {
-            perform_idle_server_maintenance(i);
+            perform_idle_server_maintenance(i, num_buckets);
         }
     }
 }
 
 static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
 {
+    int num_buckets = retained->num_buckets;
     int remaining_children_to_start;
-
     int i;
 
     ap_log_pid(pconf, ap_pid_fname);
@@ -2818,7 +2822,13 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
     restart_pending = shutdown_pending = 0;
     set_signals();
 
-    /* Don't thrash... */
+    /* Don't thrash since num_buckets depends on the
+     * system and the number of online CPU cores...
+     */
+    if (ap_daemons_limit < num_buckets)
+        ap_daemons_limit = num_buckets;
+    if (ap_daemons_to_start < num_buckets)
+        ap_daemons_to_start = num_buckets;
     if (min_spare_threads < threads_per_child * num_buckets)
         min_spare_threads = threads_per_child * num_buckets;
     if (max_spare_threads < min_spare_threads + threads_per_child * num_buckets)
@@ -2857,7 +2867,7 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
 
     mpm_state = AP_MPMQ_RUNNING;
 
-    server_main_loop(remaining_children_to_start);
+    server_main_loop(remaining_children_to_start, num_buckets);
     mpm_state = AP_MPMQ_STOPPING;
 
     if (shutdown_pending && !retained->is_graceful) {
@@ -2997,6 +3007,7 @@ static int event_open_logs(apr_pool_t * p, apr_pool_t * plog,
 {
     int startup = 0;
     int level_flags = 0;
+    int num_buckets = 0;
     ap_listen_rec **listen_buckets;
     apr_status_t rv;
     int i;
@@ -3019,9 +3030,9 @@ static int event_open_logs(apr_pool_t * p, apr_pool_t * plog,
     if (one_process) {
         num_buckets = 1;
     }
-    else if (!retained->is_graceful) { /* Preserve the number of buckets
-                                          on graceful restarts. */
-        num_buckets = 0;
+    else if (retained->is_graceful) {
+        /* Preserve the number of buckets on graceful restarts. */
+        num_buckets = retained->num_buckets;
     }
     if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
                                      &listen_buckets, &num_buckets))) {
@@ -3030,8 +3041,8 @@ static int event_open_logs(apr_pool_t * p, apr_pool_t * plog,
                      "could not duplicate listeners");
         return DONE;
     }
-    all_buckets = apr_pcalloc(pconf, num_buckets *
-                                     sizeof(event_child_bucket));
+
+    all_buckets = apr_pcalloc(pconf, num_buckets * sizeof(*all_buckets));
     for (i = 0; i < num_buckets; i++) {
         if (!one_process && /* no POD in one_process mode */
                 (rv = ap_mpm_podx_open(pconf, &all_buckets[i].pod))) {
@@ -3043,21 +3054,34 @@ static int event_open_logs(apr_pool_t * p, apr_pool_t * plog,
         all_buckets[i].listeners = listen_buckets[i];
     }
 
-    if (retained->idle_spawn_rate_len < num_buckets) {
-        int *new_ptr, new_len;
-        new_len = retained->idle_spawn_rate_len * 2;
-        if (new_len < num_buckets) {
-            new_len = num_buckets;
+    if (retained->max_buckets < num_buckets) {
+        int new_max, *new_ptr;
+        new_max = retained->max_buckets * 2;
+        if (new_max < num_buckets) {
+            new_max = num_buckets;
         }
-        new_ptr = (int *)apr_palloc(ap_pglobal, new_len * sizeof(int));
+        new_ptr = (int *)apr_palloc(ap_pglobal, new_max * sizeof(int));
         memcpy(new_ptr, retained->idle_spawn_rate,
-               retained->idle_spawn_rate_len * sizeof(int));
-        for (i = retained->idle_spawn_rate_len; i < new_len; i++) {
-            new_ptr[i] = 1;
-        }
-        retained->idle_spawn_rate_len = new_len;
+               retained->num_buckets * sizeof(int));
         retained->idle_spawn_rate = new_ptr;
+        retained->max_buckets = new_max;
+    }
+    if (retained->num_buckets < num_buckets) {
+        int rate_max = 1;
+        /* If new buckets are added, set their idle spawn rate to
+         * the highest so far, so that they get filled as quickly
+         * as the existing ones.
+         */
+        for (i = 0; i < retained->num_buckets; i++) {
+            if (rate_max < retained->idle_spawn_rate[i]) {
+                rate_max = retained->idle_spawn_rate[i];
+            }
+        }
+        for (/* up to date i */; i < num_buckets; i++) {
+            retained->idle_spawn_rate[i] = rate_max;
+        }
     }
+    retained->num_buckets = num_buckets;
 
     /* for skiplist */
     srand((unsigned int)apr_time_now());
@@ -3339,12 +3363,6 @@ static int event_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         ap_daemons_limit = server_limit;
     }
-    else if (ap_daemons_limit < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        ap_daemons_limit = num_buckets;
-    }
 
     /* ap_daemons_to_start > ap_daemons_limit checked in ap_mpm_run() */
     if (ap_daemons_to_start < 1) {
@@ -3359,12 +3377,6 @@ static int event_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         ap_daemons_to_start = 1;
     }
-    if (ap_daemons_to_start < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        ap_daemons_to_start = num_buckets;
-    }
 
     if (min_spare_threads < 1) {
         if (startup) {
@@ -3382,12 +3394,6 @@ static int event_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         min_spare_threads = 1;
     }
-    if (min_spare_threads < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        min_spare_threads = num_buckets;
-    }
 
     /* max_spare_threads < min_spare_threads + threads_per_child
      * checked in ap_mpm_run()
index c8c826658377d733fbd3c09068c115292a46a5cd..260e37666cd853daf2c7b40ed53d8a085ff17ab5 100644 (file)
@@ -993,7 +993,15 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
         return !OK;
     }
 
-    /* Don't thrash... */
+    /* Don't thrash since num_buckets depends on the
+     * system and the number of online CPU cores...
+     */
+    if (ap_daemons_limit < num_buckets)
+        ap_daemons_limit = num_buckets;
+    if (ap_daemons_to_start < num_buckets)
+        ap_daemons_to_start = num_buckets;
+    if (ap_daemons_min_free < num_buckets)
+        ap_daemons_min_free = num_buckets;
     if (ap_daemons_max_free < ap_daemons_min_free + num_buckets)
         ap_daemons_max_free = ap_daemons_min_free + num_buckets;
 
@@ -1324,7 +1332,7 @@ static int prefork_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
                          "could not open pipe-of-death");
             return DONE;
         }
-        /* Initialize cross-process accept lock (safe accept is needed only) */
+        /* Initialize cross-process accept lock (safe accept needed only) */
         if ((rv = SAFE_ACCEPT((apr_snprintf(id, sizeof id, "%i", i),
                                ap_proc_mutex_create(&all_buckets[i].mutex,
                                                     NULL, AP_ACCEPT_MUTEX_TYPE,
@@ -1483,12 +1491,6 @@ static int prefork_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         ap_daemons_limit = 1;
     }
-    if (ap_daemons_limit < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        ap_daemons_limit = num_buckets;
-    }
 
     /* ap_daemons_to_start > ap_daemons_limit checked in prefork_run() */
     if (ap_daemons_to_start < 1) {
@@ -1503,12 +1505,6 @@ static int prefork_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         ap_daemons_to_start = 1;
     }
-    if (ap_daemons_to_start < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        ap_daemons_to_start = num_buckets;
-    }
 
     if (ap_daemons_min_free < 1) {
         if (startup) {
@@ -1526,12 +1522,6 @@ static int prefork_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         ap_daemons_min_free = 1;
     }
-    if (ap_daemons_min_free < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        ap_daemons_min_free = num_buckets;
-    }
 
     /* ap_daemons_max_free < ap_daemons_min_free + 1 checked in prefork_run() */
 
index 0351c066d2330ce3b90b72160500c9d86ab0b732..190bc5ba93c1152170d4276d3318d3b0b888e321 100644 (file)
@@ -160,15 +160,20 @@ typedef struct worker_retained_data {
     /*
      * idle_spawn_rate is the number of children that will be spawned on the
      * next maintenance cycle if there aren't enough idle servers.  It is
-     * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
-     * without the need to spawn.
+     * maintained per listeners bucket, doubled up to MAX_SPAWN_RATE, and
+     * reset only when a cycle goes by without the need to spawn.
      */
-    int *idle_spawn_rate,
-         idle_spawn_rate_len;
+    int *idle_spawn_rate;
 #ifndef MAX_SPAWN_RATE
 #define MAX_SPAWN_RATE        (32)
 #endif
     int hold_off_on_exponential_spawning;
+    /*
+     * Current number of listeners buckets and maximum reached accross
+     * restarts (to size retained data according to dynamic num_buckets,
+     * eg. idle_spawn_rate).
+     */
+    int num_buckets, max_buckets;
 } worker_retained_data;
 static worker_retained_data *retained;
 
@@ -177,7 +182,6 @@ typedef struct worker_child_bucket {
     ap_listen_rec *listeners;
     apr_proc_mutex_t *mutex;
 } worker_child_bucket;
-static int                  num_buckets; /* Number of listeners buckets */
 static worker_child_bucket *all_buckets, /* All listeners buckets */
                            *my_bucket;   /* Current child bucket */
 
@@ -1236,7 +1240,7 @@ static void child_main(int child_num_arg, int child_bucket)
     apr_pool_create(&pchild, pconf);
 
     /* close unused listeners and pods */
-    for (i = 0; i < num_buckets; i++) {
+    for (i = 0; i < retained->num_buckets; i++) {
         if (i != child_bucket) {
             ap_close_listeners_ex(all_buckets[i].listeners);
             ap_mpm_podx_close(all_buckets[i].pod);
@@ -1476,14 +1480,14 @@ static void startup_children(int number_to_start)
         if (ap_scoreboard_image->parent[i].pid != 0) {
             continue;
         }
-        if (make_child(ap_server_conf, i, i % num_buckets) < 0) {
+        if (make_child(ap_server_conf, i, i % retained->num_buckets) < 0) {
             break;
         }
         --number_to_start;
     }
 }
 
-static void perform_idle_server_maintenance(int child_bucket)
+static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
 {
     int i, j;
     int idle_thread_count;
@@ -1680,7 +1684,7 @@ static void perform_idle_server_maintenance(int child_bucket)
     }
 }
 
-static void server_main_loop(int remaining_children_to_start)
+static void server_main_loop(int remaining_children_to_start, int num_buckets)
 {
     ap_generation_t old_gen;
     int child_slot;
@@ -1794,13 +1798,14 @@ static void server_main_loop(int remaining_children_to_start)
         }
 
         for (i = 0; i < num_buckets; i++) {
-            perform_idle_server_maintenance(i);
+            perform_idle_server_maintenance(i, num_buckets);
         }
     }
 }
 
 static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
 {
+    int num_buckets = retained->num_buckets;
     int remaining_children_to_start;
     int i;
 
@@ -1820,7 +1825,13 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
     restart_pending = shutdown_pending = 0;
     set_signals();
 
-    /* Don't thrash... */
+    /* Don't thrash since num_buckets depends on the
+     * system and the number of online CPU cores...
+     */
+    if (ap_daemons_limit < num_buckets)
+        ap_daemons_limit = num_buckets;
+    if (ap_daemons_to_start < num_buckets)
+        ap_daemons_to_start = num_buckets;
     if (min_spare_threads < threads_per_child * num_buckets)
         min_spare_threads = threads_per_child * num_buckets;
     if (max_spare_threads < min_spare_threads + threads_per_child * num_buckets)
@@ -1862,7 +1873,7 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
                 apr_proc_mutex_defname());
     mpm_state = AP_MPMQ_RUNNING;
 
-    server_main_loop(remaining_children_to_start);
+    server_main_loop(remaining_children_to_start, num_buckets);
     mpm_state = AP_MPMQ_STOPPING;
 
     if (shutdown_pending && !retained->is_graceful) {
@@ -2001,6 +2012,7 @@ static int worker_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
 {
     int startup = 0;
     int level_flags = 0;
+    int num_buckets = 0;
     ap_listen_rec **listen_buckets;
     apr_status_t rv;
     char id[16];
@@ -2024,9 +2036,9 @@ static int worker_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
     if (one_process) {
         num_buckets = 1;
     }
-    else if (!retained->is_graceful) { /* Preserve the number of buckets
-                                          on graceful restarts. */
-        num_buckets = 0;
+    else if (retained->is_graceful) {
+        /* Preserve the number of buckets on graceful restarts. */
+        num_buckets = retained->num_buckets;
     }
     if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
                                      &listen_buckets, &num_buckets))) {
@@ -2035,8 +2047,8 @@ static int worker_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
                      "could not duplicate listeners");
         return DONE;
     }
-    all_buckets = apr_pcalloc(pconf, num_buckets *
-                                     sizeof(worker_child_bucket));
+
+    all_buckets = apr_pcalloc(pconf, num_buckets * sizeof(*all_buckets));
     for (i = 0; i < num_buckets; i++) {
         if (!one_process && /* no POD in one_process mode */
                 (rv = ap_mpm_podx_open(pconf, &all_buckets[i].pod))) {
@@ -2045,7 +2057,7 @@ static int worker_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
                          "could not open pipe-of-death");
             return DONE;
         }
-        /* Initialize cross-process accept lock (safe accept is needed only) */
+        /* Initialize cross-process accept lock (safe accept needed only) */
         if ((rv = SAFE_ACCEPT((apr_snprintf(id, sizeof id, "%i", i),
                                ap_proc_mutex_create(&all_buckets[i].mutex,
                                                     NULL, AP_ACCEPT_MUTEX_TYPE,
@@ -2058,21 +2070,34 @@ static int worker_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
         all_buckets[i].listeners = listen_buckets[i];
     }
 
-    if (retained->idle_spawn_rate_len < num_buckets) {
-        int *new_ptr, new_len;
-        new_len = retained->idle_spawn_rate_len * 2;
-        if (new_len < num_buckets) {
-            new_len = num_buckets;
+    if (retained->max_buckets < num_buckets) {
+        int new_max, *new_ptr;
+        new_max = retained->max_buckets * 2;
+        if (new_max < num_buckets) {
+            new_max = num_buckets;
         }
-        new_ptr = (int *)apr_palloc(ap_pglobal, new_len * sizeof(int));
+        new_ptr = (int *)apr_palloc(ap_pglobal, new_max * sizeof(int));
         memcpy(new_ptr, retained->idle_spawn_rate,
-               retained->idle_spawn_rate_len * sizeof(int));
-        for (i = retained->idle_spawn_rate_len; i < new_len; i++) {
-            new_ptr[i] = 1;
-        }
-        retained->idle_spawn_rate_len = new_len;
+               retained->num_buckets * sizeof(int));
         retained->idle_spawn_rate = new_ptr;
+        retained->max_buckets = new_max;
     }
+    if (retained->num_buckets < num_buckets) {
+        int rate_max = 1;
+        /* If new buckets are added, set their idle spawn rate to
+         * the highest so far, so that they get filled as quickly
+         * as the existing ones.
+         */
+        for (i = 0; i < retained->num_buckets; i++) {
+            if (rate_max < retained->idle_spawn_rate[i]) {
+                rate_max = retained->idle_spawn_rate[i];
+            }
+        }
+        for (/* up to date i */; i < num_buckets; i++) {
+            retained->idle_spawn_rate[i] = rate_max;
+        }
+    }
+    retained->num_buckets = num_buckets;
 
     return OK;
 }
@@ -2334,12 +2359,6 @@ static int worker_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         ap_daemons_limit = server_limit;
     }
-    else if (ap_daemons_limit < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        ap_daemons_limit = num_buckets;
-    }
 
     /* ap_daemons_to_start > ap_daemons_limit checked in worker_run() */
     if (ap_daemons_to_start < 1) {
@@ -2354,12 +2373,6 @@ static int worker_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         ap_daemons_to_start = 1;
     }
-    if (ap_daemons_to_start < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        ap_daemons_to_start = num_buckets;
-    }
 
     if (min_spare_threads < 1) {
         if (startup) {
@@ -2377,12 +2390,6 @@ static int worker_check_config(apr_pool_t *p, apr_pool_t *plog,
         }
         min_spare_threads = 1;
     }
-    if (min_spare_threads < num_buckets) {
-        /* Don't thrash since num_buckets depends on
-         * the system and the number of CPU cores.
-         */
-        min_spare_threads = num_buckets;
-    }
 
     /* max_spare_threads < min_spare_threads + threads_per_child
      * checked in worker_run()