From 9864090e0b414b0c610cd0c33c6a49f81940bb9d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 18 Mar 2022 04:44:37 -1000 Subject: [PATCH] Cache parsing attr in LazyState (#68232) --- homeassistant/components/recorder/history.py | 12 ++++++++---- homeassistant/components/recorder/models.py | 11 ++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index 13574bc654f..c4d15863a5b 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -321,7 +321,9 @@ def _get_states_with_session( query = query.outerjoin( StateAttributes, (States.attributes_id == StateAttributes.attributes_id) ) - return [LazyState(row) for row in execute(query)] + + attr_cache = {} + return [LazyState(row, attr_cache) for row in execute(query)] def _get_single_entity_states_with_session(hass, session, utc_point_in_time, entity_id): @@ -397,15 +399,17 @@ def _sorted_states_to_dict( for ent_id, group in groupby(states, lambda state: state.entity_id): domain = split_entity_id(ent_id)[0] ent_results = result[ent_id] + attr_cache = {} + if not minimal_response or domain in NEED_ATTRIBUTE_DOMAINS: - ent_results.extend(LazyState(db_state) for db_state in group) + ent_results.extend(LazyState(db_state, attr_cache) for db_state in group) # With minimal response we only provide a native # State for the first and last response. All the states # in-between only provide the "state" and the # "last_changed". if not ent_results: - ent_results.append(LazyState(next(group))) + ent_results.append(LazyState(next(group), attr_cache)) prev_state = ent_results[-1] initial_state_count = len(ent_results) @@ -430,7 +434,7 @@ def _sorted_states_to_dict( # There was at least one state change # replace the last minimal state with # a full state - ent_results[-1] = LazyState(prev_state) + ent_results[-1] = LazyState(prev_state, attr_cache) # Filter out the empty lists if some states had 0 results. return {key: val for key, val in result.items() if val} diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 329af989568..ab874081055 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -533,9 +533,10 @@ class LazyState(State): "_last_changed", "_last_updated", "_context", + "_attr_cache", ] - def __init__(self, row): # pylint: disable=super-init-not-called + def __init__(self, row, attr_cache=None): # pylint: disable=super-init-not-called """Init the lazy state.""" self._row = row self.entity_id = self._row.entity_id @@ -544,12 +545,18 @@ class LazyState(State): self._last_changed = None self._last_updated = None self._context = None + self._attr_cache = attr_cache @property # type: ignore[override] def attributes(self): """State attributes.""" if self._attributes is None: source = self._row.shared_attrs or self._row.attributes + if self._attr_cache is not None and ( + attributes := self._attr_cache.get(source) + ): + self._attributes = attributes + return attributes if source == EMPTY_JSON_OBJECT or source is None: self._attributes = {} return self._attributes @@ -561,6 +568,8 @@ class LazyState(State): "Error converting row to state attributes: %s", self._row ) self._attributes = {} + if self._attr_cache is not None: + self._attr_cache[source] = self._attributes return self._attributes @attributes.setter