Fix race in WS command recorder/info (#139177)

* Fix race in WS command recorder/info

* Add comment

* Remove unnecessary local import
This commit is contained in:
Erik Montnemery 2025-02-24 18:55:13 +01:00 committed by GitHub
parent 79dbc70470
commit 6507955a14
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 32 additions and 28 deletions

View File

@ -8,6 +8,7 @@ import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import recorder as recorder_helper
from .util import get_instance
@ -23,27 +24,23 @@ def async_setup(hass: HomeAssistant) -> None:
vol.Required("type"): "recorder/info",
}
)
@callback
def ws_info(
@websocket_api.async_response
async def ws_info(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
) -> None:
"""Return status of the recorder."""
if instance := get_instance(hass):
backlog = instance.backlog
migration_in_progress = instance.migration_in_progress
migration_is_live = instance.migration_is_live
recording = instance.recording
# We avoid calling is_alive() as it can block waiting
# for the thread state lock which will block the event loop.
is_running = instance.is_running
max_backlog = instance.max_backlog
else:
backlog = None
migration_in_progress = False
migration_is_live = False
recording = False
is_running = False
max_backlog = None
# Wait for db_connected to ensure the recorder instance is created and the
# migration flags are set.
await hass.data[recorder_helper.DATA_RECORDER].db_connected
instance = get_instance(hass)
backlog = instance.backlog
migration_in_progress = instance.migration_in_progress
migration_is_live = instance.migration_is_live
recording = instance.recording
# We avoid calling is_alive() as it can block waiting
# for the thread state lock which will block the event loop.
is_running = instance.is_running
max_backlog = instance.max_backlog
recorder_info = {
"backlog": backlog,

View File

@ -2608,21 +2608,28 @@ async def test_recorder_info_bad_recorder_config(
assert response["result"]["thread_running"] is False
async def test_recorder_info_no_instance(
recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator
async def test_recorder_info_wait_database_connect(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
async_test_recorder: RecorderInstanceContextManager,
) -> None:
"""Test getting recorder when there is no instance."""
"""Test getting recorder info waits for recorder database connection."""
client = await hass_ws_client()
with patch(
"homeassistant.components.recorder.basic_websocket_api.get_instance",
return_value=None,
):
await client.send_json_auto_id({"type": "recorder/info"})
recorder_helper.async_initialize_recorder(hass)
await client.send_json_auto_id({"type": "recorder/info"})
async with async_test_recorder(hass):
response = await client.receive_json()
assert response["success"]
assert response["result"]["recording"] is False
assert response["result"]["thread_running"] is False
assert response["result"] == {
"backlog": ANY,
"max_backlog": 65000,
"migration_in_progress": False,
"migration_is_live": False,
"recording": True,
"thread_running": True,
}
async def test_recorder_info_migration_queue_exhausted(