Skip to content

Commit

Permalink
History performance improvements for single-entity requests (home-ass…
Browse files Browse the repository at this point in the history
…istant#8632)

* Bugfix: remove superfluous domain filter

This filter is already applied later in the function by the `filters` object, where it is conditionally applied when appropriate. This fixes the problem where we get a domain filter even when searching for a single entity_id, which needlessly harms the query's performance.

* Performance: build different query when only getting single entity

When querying the history of a single entity, we can use an entirely different method for the "synthetic zero data point" by simply sorting by date and doing a LIMIT 1. This performs thousands of times better than the multi-entity query when the current recorder_run has been going for a while.

* Add entity_id filter to single-entity request

The entity_id filter was handled inside the `filters.apply` logic which is used in most cases, BUT didn't work when no `filters` was passed in to the method. Now it'll work even if no `filters` object is passed in.

* Fix linting errors in history.py

* Undo removal of domain filter

Putting back the domain filter that was removed in 76a6371 - there are use-cases where get_states is called without a filter object, so we need the domain filter to work in those cases as well.

* Fix truncated comment
  • Loading branch information
OverloadUT authored and emlove committed Jul 26, 2017
1 parent abcfcdd commit 438edc5
Showing 1 changed file with 35 additions and 12 deletions.
47 changes: 35 additions & 12 deletions homeassistant/components/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,19 +119,42 @@ def get_states(hass, utc_point_in_time, entity_ids=None, run=None,
from sqlalchemy import and_, func

with session_scope(hass=hass) as session:
most_recent_state_ids = session.query(
func.max(States.state_id).label('max_state_id')
).filter(
(States.created >= run.start) &
(States.created < utc_point_in_time) &
(~States.domain.in_(IGNORE_DOMAINS)))
if entity_ids and len(entity_ids) == 1:
# Use an entirely different (and extremely fast) query if we only
# have a single entity id
most_recent_state_ids = session.query(
States.state_id.label('max_state_id')
).filter(
(States.created < utc_point_in_time) &
(States.entity_id.in_(entity_ids))
).order_by(
States.created.desc())

if filters:
most_recent_state_ids = filters.apply(most_recent_state_ids,
entity_ids)

most_recent_state_ids = most_recent_state_ids.limit(1)

if filters:
most_recent_state_ids = filters.apply(most_recent_state_ids,
entity_ids)

most_recent_state_ids = most_recent_state_ids.group_by(
States.entity_id).subquery()
else:
# We have more than one entity to look at (most commonly we want
# all entities,) so we need to do a search on all states since the
# last recorder run started.
most_recent_state_ids = session.query(
func.max(States.state_id).label('max_state_id')
).filter(
(States.created >= run.start) &
(States.created < utc_point_in_time) &
(~States.domain.in_(IGNORE_DOMAINS)))

if filters:
most_recent_state_ids = filters.apply(most_recent_state_ids,
entity_ids)

most_recent_state_ids = most_recent_state_ids.group_by(
States.entity_id)

most_recent_state_ids = most_recent_state_ids.subquery()

query = session.query(States).join(most_recent_state_ids, and_(
States.state_id == most_recent_state_ids.c.max_state_id))
Expand Down

0 comments on commit 438edc5

Please sign in to comment.