Ensure recorder runs are cleaned up during purge (#36989)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
pull/37017/head
J. Nick Koston 2020-06-22 22:10:05 -05:00 committed by GitHub
parent b4489e132c
commit ad6315be5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 4 deletions

View File

@ -6,7 +6,7 @@ from sqlalchemy.exc import SQLAlchemyError
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__)
@ -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"):
@ -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)

View File

@ -5,9 +5,10 @@ import unittest
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
@ -94,6 +95,32 @@ class TestRecorderPurge(unittest.TestCase):
)
)
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()
@ -127,6 +154,7 @@ class TestRecorderPurge(unittest.TestCase):
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:
@ -136,6 +164,9 @@ class TestRecorderPurge(unittest.TestCase):
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
@ -162,6 +193,9 @@ class TestRecorderPurge(unittest.TestCase):
# 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())
)
@ -175,6 +209,6 @@ class TestRecorderPurge(unittest.TestCase):
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"
)