mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 12:59:34 +00:00
Fix setup timings when config entry platform loads are not awaited (#113959)
* Move setup time logging into the context manager We were fetching the time twice but since the context manager already has the timing, move it there * remove log setup assertions from integration test * tweak logging to give us better data for tracking issues * redundant * adjust * preen * fixes * adjust * make api change internal so nobody uses it * coverage * fix test * fix more tests * coverage * more tests assuming internal calls * fix more * adjust * adjust * fix axis tests * fix broadlink -- it does not call async_forward_entry_setup * missed some * remove useless patch * rename, detect it both ways * clear * debug * try to fix * handle phase finishing out while paused * where its set does not need to know its late as that is an implemenation detail of setup * where its set does not need to know its late as that is an implemenation detail of setup * tweak * simplify * reduce complexity * revert order change as it makes review harder * revert naming changes as it makes review harder * improve comment * improve debug * late dispatch test * test the other way as well * Update setup.py * Update setup.py * Update setup.py * simplify * reduce
This commit is contained in:
@@ -10,7 +10,6 @@ import contextvars
|
||||
from enum import StrEnum
|
||||
import logging.handlers
|
||||
import time
|
||||
from timeit import default_timer as timer
|
||||
from types import ModuleType
|
||||
from typing import Any, Final, TypedDict
|
||||
|
||||
@@ -351,7 +350,6 @@ async def _async_setup_component( # noqa: C901
|
||||
},
|
||||
)
|
||||
|
||||
start = timer()
|
||||
_LOGGER.info("Setting up %s", domain)
|
||||
integration_set = {domain}
|
||||
|
||||
@@ -412,11 +410,8 @@ async def _async_setup_component( # noqa: C901
|
||||
async_notify_setup_error(hass, domain, integration.documentation)
|
||||
return False
|
||||
finally:
|
||||
end = timer()
|
||||
if warn_task:
|
||||
warn_task.cancel()
|
||||
_LOGGER.info("Setup of domain %s took %.1f seconds", domain, end - start)
|
||||
|
||||
if result is False:
|
||||
log_error("Integration failed to initialize.")
|
||||
return False
|
||||
@@ -663,6 +658,15 @@ class SetupPhases(StrEnum):
|
||||
"""Wait time for the packages to import."""
|
||||
|
||||
|
||||
def _setup_started(
|
||||
hass: core.HomeAssistant,
|
||||
) -> dict[tuple[str, str | None], float]:
|
||||
"""Return the setup started dict."""
|
||||
if DATA_SETUP_STARTED not in hass.data:
|
||||
hass.data[DATA_SETUP_STARTED] = {}
|
||||
return hass.data[DATA_SETUP_STARTED] # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def async_pause_setup(
|
||||
hass: core.HomeAssistant, phase: SetupPhases
|
||||
@@ -673,7 +677,9 @@ def async_pause_setup(
|
||||
setting up the base components so we can subtract it
|
||||
from the total setup time.
|
||||
"""
|
||||
if not (running := current_setup_group.get()):
|
||||
if not (running := current_setup_group.get()) or running not in _setup_started(
|
||||
hass
|
||||
):
|
||||
# This means we are likely in a late platform setup
|
||||
# that is running in a task so we do not want
|
||||
# to subtract out the time later as nothing is waiting
|
||||
@@ -689,6 +695,13 @@ def async_pause_setup(
|
||||
integration, group = running
|
||||
# Add negative time for the time we waited
|
||||
_setup_times(hass)[integration][group][phase] = -time_taken
|
||||
_LOGGER.debug(
|
||||
"Adding wait for %s for %s (%s) of %.2f",
|
||||
phase,
|
||||
integration,
|
||||
group,
|
||||
time_taken,
|
||||
)
|
||||
|
||||
|
||||
def _setup_times(
|
||||
@@ -726,8 +739,7 @@ def async_start_setup(
|
||||
yield
|
||||
return
|
||||
|
||||
setup_started: dict[tuple[str, str | None], float]
|
||||
setup_started = hass.data.setdefault(DATA_SETUP_STARTED, {})
|
||||
setup_started = _setup_started(hass)
|
||||
current = (integration, group)
|
||||
if current in setup_started:
|
||||
# We are already inside another async_start_setup, this like means we
|
||||
@@ -745,7 +757,26 @@ def async_start_setup(
|
||||
finally:
|
||||
time_taken = time.monotonic() - started
|
||||
del setup_started[current]
|
||||
_setup_times(hass)[integration][group][phase] = time_taken
|
||||
group_setup_times = _setup_times(hass)[integration][group]
|
||||
# We may see the phase multiple times if there are multiple
|
||||
# platforms, but we only care about the longest time.
|
||||
group_setup_times[phase] = max(group_setup_times[phase], time_taken)
|
||||
if group is None:
|
||||
_LOGGER.info(
|
||||
"Setup of domain %s took %.2f seconds", integration, time_taken
|
||||
)
|
||||
elif _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
wait_time = -sum(value for value in group_setup_times.values() if value < 0)
|
||||
calculated_time = time_taken - wait_time
|
||||
_LOGGER.debug(
|
||||
"Phase %s for %s (%s) took %.2fs (elapsed=%.2fs) (wait_time=%.2fs)",
|
||||
phase,
|
||||
integration,
|
||||
group,
|
||||
calculated_time,
|
||||
time_taken,
|
||||
wait_time,
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
|
||||
Reference in New Issue
Block a user