]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
master: Kill extra idling processes.
authorTimo Sirainen <tss@iki.fi>
Tue, 8 Sep 2009 21:49:08 +0000 (17:49 -0400)
committerTimo Sirainen <tss@iki.fi>
Tue, 8 Sep 2009 21:49:08 +0000 (17:49 -0400)
--HG--
branch : HEAD

src/master/service-monitor.c
src/master/service-process.c
src/master/service-process.h

index a8765d73bf34f5293ca5a105df6c39d66ed99b26..ccb50b1b116383e7dd5bacad6eca1c118e7813f0 100644 (file)
 #include "service-log.h"
 #include "service-monitor.h"
 
+#include <stdlib.h>
 #include <unistd.h>
 #include <sys/wait.h>
 #include <syslog.h>
 
+#define SERVICE_PROCESS_KILL_IDLE_MSECS (1000*60)
 #define SERVICE_STARTUP_FAILURE_THROTTLE_SECS 60
 
 static void service_monitor_start_extra_avail(struct service *service);
 
+static void service_process_kill_idle(struct service_process *process)
+{
+       struct service *service = process->service;
+
+       if (service->process_avail <= service->set->process_min_avail) {
+               /* we don't have any extra idling processes */
+               timeout_remove(&process->to_idle);
+       } else {
+               if (kill(process->pid, SIGINT) < 0 && errno != ESRCH) {
+                       service_error(service, "kill(%s, SIGINT) failed: %m",
+                                     dec2str(process->pid));
+               }
+       }
+}
+
+static void service_status_more(struct service_process *process,
+                               const struct master_status *status)
+{
+       struct service *service = process->service;
+
+       process->total_count +=
+               process->available_count - status->available_count;
+       process->idle_start = 0;
+
+       if (process->to_idle != NULL)
+               timeout_remove(&process->to_idle);
+
+       if (status->available_count != 0)
+               return;
+
+       /* process used up all of its clients */
+       i_assert(service->process_avail > 0);
+       service->process_avail--;
+
+       /* we may need to start more  */
+       service_monitor_start_extra_avail(service);
+       service_monitor_listen_start(service);
+}
+
+static void service_status_less(struct service_process *process,
+                               const struct master_status *status)
+{
+       struct service *service = process->service;
+
+       if (process->available_count == 0) {
+               /* process can accept more clients again */
+               if (service->process_avail++ == 0)
+                       service_monitor_listen_stop(service);
+               i_assert(service->process_avail <= service->process_count);
+       }
+       if (status->available_count == service->client_limit) {
+               process->idle_start = ioloop_time;
+               if (service->process_avail > service->set->process_min_avail &&
+                   process->to_idle == NULL) {
+                       /* we have more processes than we really need.
+                          add a bit of randomness so that we don't send the
+                          signal to all of them at once */
+                       process->to_idle =
+                               timeout_add(SERVICE_PROCESS_KILL_IDLE_MSECS +
+                                           (rand() % 100)*10,
+                                           service_process_kill_idle,
+                                           process);
+               }
+       }
+}
+
 static void service_status_input(struct service *service)
 {
         struct master_status status;
@@ -74,27 +142,11 @@ static void service_status_input(struct service *service)
                return;
 
        if (process->available_count > status.available_count) {
-               /* process started servicing requests */
-               process->total_count +=
-                       process->available_count - status.available_count;
-               if (status.available_count == 0) {
-                       i_assert(service->process_avail > 0);
-                       service->process_avail--;
-
-                       service_monitor_start_extra_avail(service);
-                       service_monitor_listen_start(service);
-               }
-               process->idle_start = 0;
+               /* process started servicing some more clients */
+               service_status_more(process, &status);
        } else {
-               /* process finished servicing requests */
-               if (process->available_count == 0) {
-                       if (service->process_avail++ == 0)
-                                service_monitor_listen_stop(service);
-                       i_assert(service->process_avail <=
-                                service->process_count);
-               }
-               if (status.available_count == service->client_limit)
-                       process->idle_start = ioloop_time;
+               /* process finished servicing some clients */
+               service_status_less(process, &status);
        }
        process->available_count = status.available_count;
 }
@@ -113,10 +165,11 @@ static void service_accept(struct service *service)
        i_assert(service->process_avail == 0);
 
        if (service->process_count == service->process_limit) {
-               /* we've reached our limits, new connections will have to
+               /* we've reached our limits, new clients will have to
                   wait until there are more processes available */
                i_warning("service(%s): process_limit reached, "
-                         "connections are being dropped", service->set->name);
+                         "client connections are being dropped",
+                         service->set->name);
                service->listen_pending = TRUE;
                 service_monitor_listen_stop(service);
                return;
index 9e4463c4831e8e63558f16ba2aacbe09e6ee498b..957ebb32f4de15bb46340a736934a62a4836c0b8 100644 (file)
@@ -547,6 +547,8 @@ void service_process_destroy(struct service_process *process)
 
        if (process->to_status != NULL)
                timeout_remove(&process->to_status);
+       if (process->to_idle != NULL)
+               timeout_remove(&process->to_idle);
 
        switch (process->service->type) {
        case SERVICE_TYPE_AUTH_SERVER:
index 63897a813bed99246bf87428ea47da0e197925f6..0c5270082a654f363e4bfb3e9d193a8f0b2a0ee8 100644 (file)
@@ -16,6 +16,8 @@ struct service_process {
 
        /* time when process started idling, or 0 if we're not idling */
        time_t idle_start;
+       /* kill process if it hits idle timeout */
+       struct timeout *to_idle;
 
        /* kill the process if it doesn't send initial status notification */
        struct timeout *to_status;