mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Proceed with startup if an integration setup blocks for more than 30m (#36082)
* Proceed with startup if an integration setup blocks for more than 30m * Fix test location * Fix log call * naming * revert * do not shield from cancelation * Adjust test since we now cancel when we hit the timeout
This commit is contained in:
parent
6fbc3b54bd
commit
f626129e2b
@ -422,4 +422,5 @@ async def _async_set_up_integrations(
|
||||
await async_setup_multi_components(stage_2_domains)
|
||||
|
||||
# Wrap up startup
|
||||
_LOGGER.debug("Waiting for startup to wrap up")
|
||||
await hass.async_block_till_done()
|
||||
|
@ -19,6 +19,9 @@ DATA_SETUP = "setup_tasks"
|
||||
DATA_DEPS_REQS = "deps_reqs_processed"
|
||||
|
||||
SLOW_SETUP_WARNING = 10
|
||||
# Since a pip install can run, we wait
|
||||
# 30 minutes to timeout
|
||||
SLOW_SETUP_MAX_WAIT = 1800
|
||||
|
||||
|
||||
def setup_component(hass: core.HomeAssistant, domain: str, config: ConfigType) -> bool:
|
||||
@ -167,16 +170,28 @@ async def _async_setup_component(
|
||||
|
||||
try:
|
||||
if hasattr(component, "async_setup"):
|
||||
result = await component.async_setup( # type: ignore
|
||||
task = component.async_setup( # type: ignore
|
||||
hass, processed_config
|
||||
)
|
||||
elif hasattr(component, "setup"):
|
||||
result = await hass.async_add_executor_job(
|
||||
component.setup, hass, processed_config # type: ignore
|
||||
# This should not be replaced with hass.async_add_executor_job because
|
||||
# we don't want to track this task in case it blocks startup.
|
||||
task = hass.loop.run_in_executor(
|
||||
None, component.setup, hass, processed_config # type: ignore
|
||||
)
|
||||
else:
|
||||
log_error("No setup function defined.")
|
||||
return False
|
||||
|
||||
result = await asyncio.wait_for(task, SLOW_SETUP_MAX_WAIT)
|
||||
except asyncio.TimeoutError:
|
||||
_LOGGER.error(
|
||||
"Setup of %s is taking longer than %s seconds."
|
||||
" Startup will proceed without waiting any longer.",
|
||||
domain,
|
||||
SLOW_SETUP_MAX_WAIT,
|
||||
)
|
||||
return False
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Error during setup of component %s", domain)
|
||||
async_notify_setup_error(hass, domain, integration.documentation)
|
||||
|
@ -187,8 +187,8 @@ async def test_platform_warn_slow_setup(hass):
|
||||
assert mock_call.called
|
||||
|
||||
# mock_calls[0] is the warning message for component setup
|
||||
# mock_calls[3] is the warning message for platform setup
|
||||
timeout, logger_method = mock_call.mock_calls[3][1][:2]
|
||||
# mock_calls[5] is the warning message for platform setup
|
||||
timeout, logger_method = mock_call.mock_calls[5][1][:2]
|
||||
|
||||
assert timeout == entity_platform.SLOW_SETUP_WARNING
|
||||
assert logger_method == _LOGGER.warning
|
||||
|
@ -489,13 +489,16 @@ async def test_component_warn_slow_setup(hass):
|
||||
result = await setup.async_setup_component(hass, "test_component1", {})
|
||||
assert result
|
||||
assert mock_call.called
|
||||
assert len(mock_call.mock_calls) == 3
|
||||
|
||||
assert len(mock_call.mock_calls) == 5
|
||||
timeout, logger_method = mock_call.mock_calls[0][1][:2]
|
||||
|
||||
assert timeout == setup.SLOW_SETUP_WARNING
|
||||
assert logger_method == setup._LOGGER.warning
|
||||
|
||||
timeout, function = mock_call.mock_calls[1][1][:2]
|
||||
assert timeout == setup.SLOW_SETUP_MAX_WAIT
|
||||
|
||||
assert mock_call().cancel.called
|
||||
|
||||
|
||||
@ -507,7 +510,26 @@ async def test_platform_no_warn_slow(hass):
|
||||
with patch.object(hass.loop, "call_later") as mock_call:
|
||||
result = await setup.async_setup_component(hass, "test_component1", {})
|
||||
assert result
|
||||
assert not mock_call.called
|
||||
timeout, function = mock_call.mock_calls[0][1][:2]
|
||||
assert timeout == setup.SLOW_SETUP_MAX_WAIT
|
||||
|
||||
|
||||
async def test_platform_error_slow_setup(hass, caplog):
|
||||
"""Don't block startup more than SLOW_SETUP_MAX_WAIT."""
|
||||
|
||||
with patch.object(setup, "SLOW_SETUP_MAX_WAIT", 1):
|
||||
called = []
|
||||
|
||||
async def async_setup(*args):
|
||||
"""Tracking Setup."""
|
||||
called.append(1)
|
||||
await asyncio.sleep(2)
|
||||
|
||||
mock_integration(hass, MockModule("test_component1", async_setup=async_setup))
|
||||
result = await setup.async_setup_component(hass, "test_component1", {})
|
||||
assert len(called) == 1
|
||||
assert not result
|
||||
assert "test_component1 is taking longer than 1 seconds" in caplog.text
|
||||
|
||||
|
||||
async def test_when_setup_already_loaded(hass):
|
||||
|
Loading…
x
Reference in New Issue
Block a user