Skip to content

Commit

Permalink
Ensure recorder runs are cleaned up during purge (home-assistant#36989)
Browse files Browse the repository at this point in the history
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
  • Loading branch information
bdraco and balloob authored Jun 23, 2020
1 parent b4489e1 commit ad6315b
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 4 deletions.
11 changes: 9 additions & 2 deletions homeassistant/components/recorder/purge.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import homeassistant.util.dt as dt_util

from .models import Events, States
from .models import Events, RecorderRuns, States
from .util import session_scope

_LOGGER = logging.getLogger(__name__)
Expand All @@ -33,6 +33,13 @@ def purge_old_data(instance, purge_days, repack):
)
_LOGGER.debug("Deleted %s events", deleted_rows)

deleted_rows = (
session.query(RecorderRuns)
.filter(RecorderRuns.start < purge_before)
.delete(synchronize_session=False)
)
_LOGGER.debug("Deleted %s recorder_runs", deleted_rows)

if repack:
# Execute sqlite or postgresql vacuum command to free up space on disk
if instance.engine.driver in ("pysqlite", "postgresql"):
Expand All @@ -41,7 +48,7 @@ def purge_old_data(instance, purge_days, repack):
# Optimize mysql / mariadb tables to free up space on disk
elif instance.engine.driver == "mysqldb":
_LOGGER.debug("Optimizing SQL DB to free space")
instance.engine.execute("OPTIMIZE TABLE states, events")
instance.engine.execute("OPTIMIZE TABLE states, events, recorder_runs")

except SQLAlchemyError as err:
_LOGGER.warning("Error purging history: %s.", err)
38 changes: 36 additions & 2 deletions tests/components/recorder/test_purge.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

from homeassistant.components import recorder
from homeassistant.components.recorder.const import DATA_INSTANCE
from homeassistant.components.recorder.models import Events, States
from homeassistant.components.recorder.models import Events, RecorderRuns, States
from homeassistant.components.recorder.purge import purge_old_data
from homeassistant.components.recorder.util import session_scope
from homeassistant.util import dt as dt_util

from tests.async_mock import patch
from tests.common import get_test_home_assistant, init_recorder_component
Expand Down Expand Up @@ -94,6 +95,32 @@ def _add_test_events(self):
)
)

def _add_test_recorder_runs(self):
"""Add a few recorder_runs for testing."""
now = datetime.now()
five_days_ago = now - timedelta(days=5)
eleven_days_ago = now - timedelta(days=11)

self.hass.block_till_done()
self.hass.data[DATA_INSTANCE].block_till_done()

with recorder.session_scope(hass=self.hass) as session:
for rec_id in range(6):
if rec_id < 2:
timestamp = eleven_days_ago
elif rec_id < 4:
timestamp = five_days_ago
else:
timestamp = now

session.add(
RecorderRuns(
start=timestamp,
created=dt_util.utcnow(),
end=timestamp + timedelta(days=1),
)
)

def test_purge_old_states(self):
"""Test deleting old states."""
self._add_test_states()
Expand Down Expand Up @@ -127,6 +154,7 @@ def test_purge_method(self):
service_data = {"keep_days": 4}
self._add_test_events()
self._add_test_states()
self._add_test_recorder_runs()

# make sure we start with 6 states
with session_scope(hass=self.hass) as session:
Expand All @@ -136,6 +164,9 @@ def test_purge_method(self):
events = session.query(Events).filter(Events.event_type.like("EVENT_TEST%"))
assert events.count() == 6

recorder_runs = session.query(RecorderRuns)
assert recorder_runs.count() == 7

self.hass.data[DATA_INSTANCE].block_till_done()

# run purge method - no service data, use defaults
Expand All @@ -162,6 +193,9 @@ def test_purge_method(self):
# now we should only have 2 events left
assert events.count() == 2

# now we should only have 3 recorder runs left
assert recorder_runs.count() == 3

assert not (
"EVENT_TEST_PURGE" in (event.event_type for event in events.all())
)
Expand All @@ -175,6 +209,6 @@ def test_purge_method(self):
self.hass.block_till_done()
self.hass.data[DATA_INSTANCE].block_till_done()
assert (
mock_logger.debug.mock_calls[3][1][0]
mock_logger.debug.mock_calls[4][1][0]
== "Vacuuming SQL DB to free space"
)

0 comments on commit ad6315b

Please sign in to comment.