changeset 9906:29ebf1c9ff26 HEAD

master: Kill extra idling processes.
author Timo Sirainen <tss@iki.fi>
date Tue, 08 Sep 2009 17:49:08 -0400
parents 54c0c2c24f2c
children 3265d6e98c46
files src/master/service-monitor.c src/master/service-process.c src/master/service-process.h
diffstat 3 files changed, 79 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/src/master/service-monitor.c	Tue Sep 08 14:49:35 2009 -0400
+++ b/src/master/service-monitor.c	Tue Sep 08 17:49:08 2009 -0400
@@ -11,14 +11,82 @@
 #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 @@
 		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 @@
 	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;
--- a/src/master/service-process.c	Tue Sep 08 14:49:35 2009 -0400
+++ b/src/master/service-process.c	Tue Sep 08 17:49:08 2009 -0400
@@ -547,6 +547,8 @@
 
 	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:
--- a/src/master/service-process.h	Tue Sep 08 14:49:35 2009 -0400
+++ b/src/master/service-process.h	Tue Sep 08 17:49:08 2009 -0400
@@ -16,6 +16,8 @@
 
 	/* 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;