Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions homeassistant/components/recorder/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -989,8 +989,10 @@ def _commit_event_session(self) -> None:

def _handle_sqlite_corruption(self) -> None:
"""Handle the sqlite3 database being corrupt."""
self._close_event_session()
self._close_connection()
try:
self._close_event_session()
finally:
self._close_connection()
move_away_broken_database(dburl_to_path(self.db_url))
self.run_history.reset()
self._setup_recorder()
Expand Down Expand Up @@ -1213,18 +1215,21 @@ def _end_session(self) -> None:
"""End the recorder session."""
if self.event_session is None:
return
try:
if self.run_history.active:
self.run_history.end(self.event_session)
try:
self._commit_event_session_or_retry()
self.event_session.close()
except Exception as err: # pylint: disable=broad-except
_LOGGER.exception("Error saving the event session during shutdown: %s", err)

self.event_session.close()
self.run_history.clear()

def _shutdown(self) -> None:
"""Save end time for current run."""
self.hass.add_job(self._async_stop_listeners)
self._stop_executor()
self._end_session()
self._close_connection()
try:
self._end_session()
finally:
self._close_connection()
10 changes: 7 additions & 3 deletions homeassistant/components/recorder/run_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ def current(self) -> RecorderRuns:
start=self.recording_start, created=dt_util.utcnow()
)

@property
def active(self) -> bool:
"""Return if a run is active."""
return self._current_run_info is not None

def get(self, start: datetime) -> RecorderRuns | None:
"""Return the recorder run that started before or at start.

Expand Down Expand Up @@ -142,6 +147,5 @@ def clear(self) -> None:

Must run in the recorder thread.
"""
assert self._current_run_info is not None
assert self._current_run_info.end is not None
self._current_run_info = None
if self._current_run_info:
self._current_run_info = None
34 changes: 29 additions & 5 deletions tests/components/recorder/test_run_history.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
"""Test run history."""

from datetime import timedelta
from unittest.mock import patch

from homeassistant.components import recorder
from homeassistant.components.recorder.db_schema import RecorderRuns
from homeassistant.components.recorder.models import process_timestamp
from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util

from tests.common import SetupRecorderInstanceT


async def test_run_history(recorder_mock, hass):
"""Test the run history gives the correct run."""
Expand Down Expand Up @@ -47,12 +51,32 @@ async def test_run_history(recorder_mock, hass):
)


async def test_run_history_during_schema_migration(recorder_mock, hass):
"""Test the run history during schema migration."""
instance = recorder.get_instance(hass)
async def test_run_history_while_recorder_is_not_yet_started(
async_setup_recorder_instance: SetupRecorderInstanceT,
hass: HomeAssistant,
recorder_db_url: str,
) -> None:
"""Test the run history while recorder is not yet started.

This usually happens during schema migration because
we do not start right away.
"""
# Prevent the run history from starting to ensure
# we can test run_history.current.start returns the expected value
with patch(
"homeassistant.components.recorder.run_history.RunHistory.start",
):
instance = await async_setup_recorder_instance(hass)
run_history = instance.run_history
assert run_history.current.start == run_history.recording_start
with instance.get_session() as session:
run_history.start(session)

def _start_run_history():
with instance.get_session() as session:
run_history.start(session)

# Ideally we would run run_history.start in the recorder thread
# but since we mocked it out above, we run it directly here
# via the database executor to avoid blocking the event loop.
await instance.async_add_executor_job(_start_run_history)
assert run_history.current.start == run_history.recording_start
assert run_history.current.created >= run_history.recording_start