Prevent parallel reload of automations (#50008)

This commit is contained in:
Erik Montnemery 2021-05-25 17:50:50 +02:00 committed by GitHub
parent d2804433d3
commit 7b5e63132c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 2 deletions

View file

@ -54,7 +54,10 @@ from homeassistant.helpers.script import (
Script,
)
from homeassistant.helpers.script_variables import ScriptVariables
from homeassistant.helpers.service import async_register_admin_service
from homeassistant.helpers.service import (
ReloadServiceHelper,
async_register_admin_service,
)
from homeassistant.helpers.trace import (
TraceElement,
script_execution_set,
@ -253,8 +256,14 @@ async def async_setup(hass, config):
await _async_process_config(hass, conf, component)
hass.bus.async_fire(EVENT_AUTOMATION_RELOADED, context=service_call.context)
reload_helper = ReloadServiceHelper(reload_service_handler)
async_register_admin_service(
hass, DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=vol.Schema({})
hass,
DOMAIN,
SERVICE_RELOAD,
reload_helper.execute_service,
schema=vol.Schema({}),
)
return True

View file

@ -783,3 +783,43 @@ def verify_domain_control(
return check_permissions
return decorator
class ReloadServiceHelper:
"""Helper for reload services to minimize unnecessary reloads."""
def __init__(self, service_func: Callable[[ServiceCall], Awaitable]):
"""Initialize ReloadServiceHelper."""
self._service_func = service_func
self._service_running = False
self._service_condition = asyncio.Condition()
async def execute_service(self, service_call: ServiceCall) -> None:
"""Execute the service.
If a previous reload task if currently in progress, wait for it to finish first.
Once the previous reload task has finished, one of the waiting tasks will be
assigned to execute the reload, the others will wait for the reload to finish.
"""
do_reload = False
async with self._service_condition:
if self._service_running:
# A previous reload task is already in progress, wait for it to finish
await self._service_condition.wait()
async with self._service_condition:
if not self._service_running:
# This task will do the reload
self._service_running = True
do_reload = True
else:
# Another task will perform the reload, wait for it to finish
await self._service_condition.wait()
if do_reload:
# Reload, then notify other tasks
await self._service_func(service_call)
async with self._service_condition:
self._service_running = False
self._service_condition.notify_all()