Schedule polling as periodic tasks (#112640)
* Schedule periodic coordinator updates as background tasks. Currently, the coordinator's periodic refreshes delay startup because they are not scheduled as background tasks. We will wait if the startup takes long enough for the first planned refresh. Another coordinator's scheduled refresh will be fired on busy systems, further delaying the startup. This chain of events results in the startup taking a long time and hitting the safety timeout because too many coordinators are refreshing. This case can also happen with scheduled entity refreshes, but it's less common. A future PR will address that case. * periodic_tasks * periodic_tasks * periodic_tasks * merge * merge * merge * merge * merge * fix test that call the sync api from async * one more place * cannot chain * async_run_periodic_hass_job * sun and pattern time changes from automations also block startup * Revert "sun and pattern time changes from automations also block startup" This reverts commit6de2defa05
. * make sure polling is cancelled when config entry is unloaded * Revert "Revert "sun and pattern time changes from automations also block startup"" This reverts commite8f12aad55
. * remove DisabledError from homewizard test as it relies on a race * fix race * direct coverage
This commit is contained in:
parent
5da629b3e5
commit
a6b17dbe68
12 changed files with 292 additions and 51 deletions
|
@ -17,6 +17,7 @@ from contextvars import ContextVar
|
|||
from copy import deepcopy
|
||||
from enum import Enum, StrEnum
|
||||
import functools
|
||||
from itertools import chain
|
||||
import logging
|
||||
from random import randint
|
||||
from types import MappingProxyType
|
||||
|
@ -377,6 +378,7 @@ class ConfigEntry:
|
|||
|
||||
self._tasks: set[asyncio.Future[Any]] = set()
|
||||
self._background_tasks: set[asyncio.Future[Any]] = set()
|
||||
self._periodic_tasks: set[asyncio.Future[Any]] = set()
|
||||
|
||||
self._integration_for_domain: loader.Integration | None = None
|
||||
self._tries = 0
|
||||
|
@ -854,15 +856,15 @@ class ConfigEntry:
|
|||
if job := self._on_unload.pop()():
|
||||
self.async_create_task(hass, job)
|
||||
|
||||
if not self._tasks and not self._background_tasks:
|
||||
if not self._tasks and not self._background_tasks and not self._periodic_tasks:
|
||||
return
|
||||
|
||||
cancel_message = f"Config entry {self.title} with {self.domain} unloading"
|
||||
for task in self._background_tasks:
|
||||
for task in chain(self._background_tasks, self._periodic_tasks):
|
||||
task.cancel(cancel_message)
|
||||
|
||||
_, pending = await asyncio.wait(
|
||||
[*self._tasks, *self._background_tasks], timeout=10
|
||||
[*self._tasks, *self._background_tasks, *self._periodic_tasks], timeout=10
|
||||
)
|
||||
|
||||
for task in pending:
|
||||
|
@ -1026,7 +1028,13 @@ class ConfigEntry:
|
|||
|
||||
Background tasks are automatically canceled when config entry is unloaded.
|
||||
|
||||
target: target to call.
|
||||
A background task is different from a normal task:
|
||||
|
||||
- Will not block startup
|
||||
- Will be automatically cancelled on shutdown
|
||||
- Calls to async_block_till_done will not wait for completion
|
||||
|
||||
This method must be run in the event loop.
|
||||
"""
|
||||
task = hass.async_create_background_task(target, name, eager_start)
|
||||
if task.done():
|
||||
|
@ -1035,6 +1043,35 @@ class ConfigEntry:
|
|||
task.add_done_callback(self._background_tasks.remove)
|
||||
return task
|
||||
|
||||
@callback
|
||||
def async_create_periodic_task(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
target: Coroutine[Any, Any, _R],
|
||||
name: str,
|
||||
eager_start: bool = False,
|
||||
) -> asyncio.Task[_R]:
|
||||
"""Create a periodic task tied to the config entry lifecycle.
|
||||
|
||||
Periodic tasks are automatically canceled when config entry is unloaded.
|
||||
|
||||
This type of task is typically used for polling.
|
||||
|
||||
A periodic task is different from a normal task:
|
||||
|
||||
- Will not block startup
|
||||
- Will be automatically cancelled on shutdown
|
||||
- Calls to async_block_till_done will wait for completion by default
|
||||
|
||||
This method must be run in the event loop.
|
||||
"""
|
||||
task = hass.async_create_periodic_task(target, name, eager_start)
|
||||
if task.done():
|
||||
return task
|
||||
self._periodic_tasks.add(task)
|
||||
task.add_done_callback(self._periodic_tasks.remove)
|
||||
return task
|
||||
|
||||
|
||||
current_entry: ContextVar[ConfigEntry | None] = ContextVar(
|
||||
"current_entry", default=None
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue