From 0e2d98dbf56c7476b44953e383d80cd066d5c9e1 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Fri, 16 Feb 2018 05:22:57 +0100 Subject: [PATCH] Optimize recorder purge (#12448) --- homeassistant/components/recorder/purge.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index d4b07232436..81c28bb94d9 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -12,8 +12,7 @@ _LOGGER = logging.getLogger(__name__) def purge_old_data(instance, purge_days, repack): """Purge events and states older than purge_days ago.""" from .models import States, Events - from sqlalchemy import orm - from sqlalchemy.sql import exists + from sqlalchemy import func purge_before = dt_util.utcnow() - timedelta(days=purge_days) _LOGGER.debug("Purging events before %s", purge_before) @@ -22,18 +21,10 @@ def purge_old_data(instance, purge_days, repack): # For each entity, the most recent state is protected from deletion # s.t. we can properly restore state even if the entity has not been # updated in a long time - states_alias = orm.aliased(States, name='StatesAlias') - protected_states = session.query(States.state_id, States.event_id)\ - .filter(~exists() - .where(States.entity_id == - states_alias.entity_id) - .where(states_alias.last_updated > - States.last_updated))\ - .all() + protected_states = session.query(func.max(States.state_id)) \ + .group_by(States.entity_id).all() protected_state_ids = tuple((state[0] for state in protected_states)) - protected_event_ids = tuple((state[1] for state in protected_states - if state[1] is not None)) deleted_rows = session.query(States) \ .filter((States.last_updated < purge_before)) \ @@ -46,6 +37,13 @@ def purge_old_data(instance, purge_days, repack): # Otherwise, if the SQL server has "ON DELETE CASCADE" as default, it # will delete the protected state when deleting its associated # event. Also, we would be producing NULLed foreign keys otherwise. + protected_events = session.query(States.event_id) \ + .filter(States.state_id.in_(protected_state_ids)) \ + .filter(States.event_id.isnot(None)) \ + .all() + + protected_event_ids = tuple((state[0] for state in protected_events)) + deleted_rows = session.query(Events) \ .filter((Events.time_fired < purge_before)) \ .filter(~Events.event_id.in_(