mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +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)
|
await async_setup_multi_components(stage_2_domains)
|
||||||
|
|
||||||
# Wrap up startup
|
# Wrap up startup
|
||||||
|
_LOGGER.debug("Waiting for startup to wrap up")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -19,6 +19,9 @@ DATA_SETUP = "setup_tasks"
|
|||||||
DATA_DEPS_REQS = "deps_reqs_processed"
|
DATA_DEPS_REQS = "deps_reqs_processed"
|
||||||
|
|
||||||
SLOW_SETUP_WARNING = 10
|
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:
|
def setup_component(hass: core.HomeAssistant, domain: str, config: ConfigType) -> bool:
|
||||||
@ -167,16 +170,28 @@ async def _async_setup_component(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if hasattr(component, "async_setup"):
|
if hasattr(component, "async_setup"):
|
||||||
result = await component.async_setup( # type: ignore
|
task = component.async_setup( # type: ignore
|
||||||
hass, processed_config
|
hass, processed_config
|
||||||
)
|
)
|
||||||
elif hasattr(component, "setup"):
|
elif hasattr(component, "setup"):
|
||||||
result = await hass.async_add_executor_job(
|
# This should not be replaced with hass.async_add_executor_job because
|
||||||
component.setup, hass, processed_config # type: ignore
|
# 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:
|
else:
|
||||||
log_error("No setup function defined.")
|
log_error("No setup function defined.")
|
||||||
return False
|
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
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.exception("Error during setup of component %s", domain)
|
_LOGGER.exception("Error during setup of component %s", domain)
|
||||||
async_notify_setup_error(hass, domain, integration.documentation)
|
async_notify_setup_error(hass, domain, integration.documentation)
|
||||||
|
@ -187,8 +187,8 @@ async def test_platform_warn_slow_setup(hass):
|
|||||||
assert mock_call.called
|
assert mock_call.called
|
||||||
|
|
||||||
# mock_calls[0] is the warning message for component setup
|
# mock_calls[0] is the warning message for component setup
|
||||||
# mock_calls[3] is the warning message for platform setup
|
# mock_calls[5] is the warning message for platform setup
|
||||||
timeout, logger_method = mock_call.mock_calls[3][1][:2]
|
timeout, logger_method = mock_call.mock_calls[5][1][:2]
|
||||||
|
|
||||||
assert timeout == entity_platform.SLOW_SETUP_WARNING
|
assert timeout == entity_platform.SLOW_SETUP_WARNING
|
||||||
assert logger_method == _LOGGER.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", {})
|
result = await setup.async_setup_component(hass, "test_component1", {})
|
||||||
assert result
|
assert result
|
||||||
assert mock_call.called
|
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]
|
timeout, logger_method = mock_call.mock_calls[0][1][:2]
|
||||||
|
|
||||||
assert timeout == setup.SLOW_SETUP_WARNING
|
assert timeout == setup.SLOW_SETUP_WARNING
|
||||||
assert logger_method == setup._LOGGER.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
|
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:
|
with patch.object(hass.loop, "call_later") as mock_call:
|
||||||
result = await setup.async_setup_component(hass, "test_component1", {})
|
result = await setup.async_setup_component(hass, "test_component1", {})
|
||||||
assert result
|
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):
|
async def test_when_setup_already_loaded(hass):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user