Refactor integration startup time to show wall clock time (#113707)

* Refactor setup time tracking to exclude time waiting on other operations

We now exclude the import time and th time waiting on
base platforms to setup from the setup times

* tweak

* tweak

* tweak

* tweak

* adjust

* fixes

* fixes

* preen

* preen

* tweak

* tweak

* adjust

* tweak

* reduce

* do not count integrtion platforms against their parent integration

* handle legacy tts platforms

* stt as well

* one more wait

* use the same pattern in all the legacy

* fix tts and stt legacy

* fix

* fix

* reduce

* preen

* entity comp does not wait for platforms

* scene blocks as well

* fix test

* test fixes

* coverage

* coverage

* coverage

* fix test

* Update tests/test_setup.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update tests/test_setup.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/setup.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* strip

* strip WAIT_PLATFORM_INTEGRATION

* strip WAIT_PLATFORM_INTEGRATION

* strip WAIT_PLATFORM_INTEGRATION

* strip WAIT_PLATFORM_INTEGRATION

* remove complexity

* Apply suggestions from code review

* no longer works that way

* fixes

* fixes

* fixes

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
J. Nick Koston 2024-03-18 15:45:34 -10:00 committed by GitHub
parent 9be5f3531f
commit c615b52840
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 598 additions and 176 deletions

View file

@ -32,7 +32,7 @@ from homeassistant.core import (
)
from homeassistant.exceptions import HomeAssistantError, PlatformNotReady
from homeassistant.generated import languages
from homeassistant.setup import async_start_setup
from homeassistant.setup import SetupPhases, async_start_setup
from homeassistant.util.async_ import create_eager_task
from . import (
@ -284,7 +284,13 @@ class EntityPlatform:
discovery_info,
)
await self._async_setup_platform(async_create_setup_awaitable)
with async_start_setup(
hass,
integration=self.platform_name,
group=str(id(platform_config)),
phase=SetupPhases.PLATFORM_SETUP,
):
await self._async_setup_platform(async_create_setup_awaitable)
@callback
def async_shutdown(self) -> None:
@ -341,81 +347,78 @@ class EntityPlatform:
self.platform_name,
SLOW_SETUP_WARNING,
)
with async_start_setup(hass, [full_name]):
try:
awaitable = async_create_setup_awaitable()
if asyncio.iscoroutine(awaitable):
awaitable = create_eager_task(awaitable)
try:
awaitable = async_create_setup_awaitable()
if asyncio.iscoroutine(awaitable):
awaitable = create_eager_task(awaitable)
async with hass.timeout.async_timeout(SLOW_SETUP_MAX_WAIT, self.domain):
await asyncio.shield(awaitable)
async with hass.timeout.async_timeout(SLOW_SETUP_MAX_WAIT, self.domain):
await asyncio.shield(awaitable)
# Block till all entities are done
while self._tasks:
# Await all tasks even if they are done
# to ensure exceptions are propagated
pending = self._tasks.copy()
self._tasks.clear()
await asyncio.gather(*pending)
# Block till all entities are done
while self._tasks:
# Await all tasks even if they are done
# to ensure exceptions are propagated
pending = self._tasks.copy()
self._tasks.clear()
await asyncio.gather(*pending)
hass.config.components.add(full_name)
self._setup_complete = True
return True
except PlatformNotReady as ex:
tries += 1
wait_time = min(tries, 6) * PLATFORM_NOT_READY_BASE_WAIT_TIME
message = str(ex)
ready_message = f"ready yet: {message}" if message else "ready yet"
if tries == 1:
logger.warning(
"Platform %s not %s; Retrying in background in %d seconds",
self.platform_name,
ready_message,
wait_time,
)
else:
logger.debug(
"Platform %s not %s; Retrying in %d seconds",
self.platform_name,
ready_message,
wait_time,
)
async def setup_again(*_args: Any) -> None:
"""Run setup again."""
self._async_cancel_retry_setup = None
await self._async_setup_platform(
async_create_setup_awaitable, tries
)
if hass.state is CoreState.running:
self._async_cancel_retry_setup = async_call_later(
hass, wait_time, setup_again
)
else:
self._async_cancel_retry_setup = hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STARTED, setup_again
)
return False
except TimeoutError:
logger.error(
(
"Setup of platform %s is taking longer than %s seconds."
" Startup will proceed without waiting any longer."
),
hass.config.components.add(full_name)
self._setup_complete = True
return True
except PlatformNotReady as ex:
tries += 1
wait_time = min(tries, 6) * PLATFORM_NOT_READY_BASE_WAIT_TIME
message = str(ex)
ready_message = f"ready yet: {message}" if message else "ready yet"
if tries == 1:
logger.warning(
"Platform %s not %s; Retrying in background in %d seconds",
self.platform_name,
SLOW_SETUP_MAX_WAIT,
ready_message,
wait_time,
)
return False
except Exception: # pylint: disable=broad-except
logger.exception(
"Error while setting up %s platform for %s",
else:
logger.debug(
"Platform %s not %s; Retrying in %d seconds",
self.platform_name,
self.domain,
ready_message,
wait_time,
)
return False
finally:
warn_task.cancel()
async def setup_again(*_args: Any) -> None:
"""Run setup again."""
self._async_cancel_retry_setup = None
await self._async_setup_platform(async_create_setup_awaitable, tries)
if hass.state is CoreState.running:
self._async_cancel_retry_setup = async_call_later(
hass, wait_time, setup_again
)
else:
self._async_cancel_retry_setup = hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STARTED, setup_again
)
return False
except TimeoutError:
logger.error(
(
"Setup of platform %s is taking longer than %s seconds."
" Startup will proceed without waiting any longer."
),
self.platform_name,
SLOW_SETUP_MAX_WAIT,
)
return False
except Exception: # pylint: disable=broad-except
logger.exception(
"Error while setting up %s platform for %s",
self.platform_name,
self.domain,
)
return False
finally:
warn_task.cancel()
async def _async_get_translations(
self, language: str, category: str, integration: str