]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
master: Fix idle-kill for processes with a single short-lived client
authorAki Tuomi <aki.tuomi@open-xchange.com>
Thu, 6 May 2021 11:29:03 +0000 (14:29 +0300)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Mon, 14 Jun 2021 13:44:01 +0000 (13:44 +0000)
lib-master sends unimportant service status updates to master only once per
second when client_limit>1, because within that 1 second the available client
count may have already changed many times. Normally this is fine, but there's a
problem with the initial client:

 * The client connection launches a new process
 * The process sends the initial status notification with available_count=MAX
 * The client connection is handled, but because the status notification was
   just sent, the update won't be sent until 1 second later
 * The client disconnects within the 1 second
 * The available_count is again MAX, so no status notification is sent

In this situation the master process never created the idle-kill timeout.
Usually the following clients will cause a status notification to be sent, but
if there was only the one client then this process won't be idle-killed. Fix the
situation so that the idle-kill timeout is created already by the initial status
notification.

src/master/service-monitor.c

index 4b12ed1d0b07f51590eaebe6f0d50daa4619689a..a0921fa9f956380b4fa81cedacfd243a08db0914 100644 (file)
@@ -88,8 +88,28 @@ static void service_status_more(struct service_process *process,
        service_monitor_listen_start(service);
 }
 
-static void service_status_less(struct service_process *process,
-                               const struct master_status *status)
+static void service_check_idle(struct service_process *process)
+{
+       struct service *service = process->service;
+
+       if (process->available_count != service->client_limit)
+               return;
+       process->idle_start = ioloop_time;
+       if (service->process_avail > service->set->process_min_avail &&
+           process->to_idle == NULL &&
+           service->idle_kill != UINT_MAX) {
+               /* 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->idle_kill * 1000) +
+                                   i_rand_limit(100) * 10,
+                                   service_process_kill_idle,
+                                   process);
+       }
+}
+
+static void service_status_less(struct service_process *process)
 {
        struct service *service = process->service;
 
@@ -102,21 +122,6 @@ static void service_status_less(struct service_process *process,
                        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 &&
-                   service->idle_kill != UINT_MAX) {
-                       /* 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->idle_kill * 1000) +
-                                           i_rand_limit(100) * 10,
-                                           service_process_kill_idle,
-                                           process);
-               }
-       }
        if (service->type == SERVICE_TYPE_LOGIN)
                service_login_notify(service, FALSE);
 }
@@ -152,17 +157,17 @@ service_status_input_one(struct service *service,
        /* first status notification */
        timeout_remove(&process->to_status);
 
-       if (process->available_count == status->available_count)
-               return;
-
-       if (process->available_count > status->available_count) {
-               /* process started servicing some more clients */
-               service_status_more(process, status);
-       } else {
-               /* process finished servicing some clients */
-               service_status_less(process, status);
+       if (process->available_count != status->available_count) {
+               if (process->available_count > status->available_count) {
+                       /* process started servicing some more clients */
+                       service_status_more(process, status);
+               } else {
+                       /* process finished servicing some clients */
+                       service_status_less(process);
+               }
+               process->available_count = status->available_count;
        }
-       process->available_count = status->available_count;
+       service_check_idle(process);
 }
 
 static void service_status_input(struct service *service)