From: Aki Tuomi Date: Thu, 6 May 2021 11:29:03 +0000 (+0300) Subject: master: Fix idle-kill for processes with a single short-lived client X-Git-Tag: 2.3.16~55 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=da607df02a060491d45af2c809ab30e839275850;p=thirdparty%2Fdovecot%2Fcore.git master: Fix idle-kill for processes with a single short-lived client 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. --- diff --git a/src/master/service-monitor.c b/src/master/service-monitor.c index 4b12ed1d0b..a0921fa9f9 100644 --- a/src/master/service-monitor.c +++ b/src/master/service-monitor.c @@ -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)