mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 02:07:09 +00:00
Reduce memory pressure from history_stats with large data sets (#73289)
This commit is contained in:
parent
211e5432ac
commit
d3f01f7ea9
@ -23,6 +23,14 @@ class HistoryStatsState:
|
|||||||
period: tuple[datetime.datetime, datetime.datetime]
|
period: tuple[datetime.datetime, datetime.datetime]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HistoryState:
|
||||||
|
"""A minimal state to avoid holding on to State objects."""
|
||||||
|
|
||||||
|
state: str
|
||||||
|
last_changed: float
|
||||||
|
|
||||||
|
|
||||||
class HistoryStats:
|
class HistoryStats:
|
||||||
"""Manage history stats."""
|
"""Manage history stats."""
|
||||||
|
|
||||||
@ -40,7 +48,7 @@ class HistoryStats:
|
|||||||
self.entity_id = entity_id
|
self.entity_id = entity_id
|
||||||
self._period = (MIN_TIME_UTC, MIN_TIME_UTC)
|
self._period = (MIN_TIME_UTC, MIN_TIME_UTC)
|
||||||
self._state: HistoryStatsState = HistoryStatsState(None, None, self._period)
|
self._state: HistoryStatsState = HistoryStatsState(None, None, self._period)
|
||||||
self._history_current_period: list[State] = []
|
self._history_current_period: list[HistoryState] = []
|
||||||
self._previous_run_before_start = False
|
self._previous_run_before_start = False
|
||||||
self._entity_states = set(entity_states)
|
self._entity_states = set(entity_states)
|
||||||
self._duration = duration
|
self._duration = duration
|
||||||
@ -103,20 +111,18 @@ class HistoryStats:
|
|||||||
<= floored_timestamp(new_state.last_changed)
|
<= floored_timestamp(new_state.last_changed)
|
||||||
<= current_period_end_timestamp
|
<= current_period_end_timestamp
|
||||||
):
|
):
|
||||||
self._history_current_period.append(new_state)
|
self._history_current_period.append(
|
||||||
|
HistoryState(
|
||||||
|
new_state.state, new_state.last_changed.timestamp()
|
||||||
|
)
|
||||||
|
)
|
||||||
new_data = True
|
new_data = True
|
||||||
if not new_data and current_period_end_timestamp < now_timestamp:
|
if not new_data and current_period_end_timestamp < now_timestamp:
|
||||||
# If period has not changed and current time after the period end...
|
# If period has not changed and current time after the period end...
|
||||||
# Don't compute anything as the value cannot have changed
|
# Don't compute anything as the value cannot have changed
|
||||||
return self._state
|
return self._state
|
||||||
else:
|
else:
|
||||||
self._history_current_period = await get_instance(
|
await self._async_history_from_db(current_period_start, current_period_end)
|
||||||
self.hass
|
|
||||||
).async_add_executor_job(
|
|
||||||
self._update_from_database,
|
|
||||||
current_period_start,
|
|
||||||
current_period_end,
|
|
||||||
)
|
|
||||||
self._previous_run_before_start = False
|
self._previous_run_before_start = False
|
||||||
|
|
||||||
hours_matched, match_count = self._async_compute_hours_and_changes(
|
hours_matched, match_count = self._async_compute_hours_and_changes(
|
||||||
@ -127,7 +133,24 @@ class HistoryStats:
|
|||||||
self._state = HistoryStatsState(hours_matched, match_count, self._period)
|
self._state = HistoryStatsState(hours_matched, match_count, self._period)
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def _update_from_database(
|
async def _async_history_from_db(
|
||||||
|
self,
|
||||||
|
current_period_start: datetime.datetime,
|
||||||
|
current_period_end: datetime.datetime,
|
||||||
|
) -> None:
|
||||||
|
"""Update history data for the current period from the database."""
|
||||||
|
instance = get_instance(self.hass)
|
||||||
|
states = await instance.async_add_executor_job(
|
||||||
|
self._state_changes_during_period,
|
||||||
|
current_period_start,
|
||||||
|
current_period_end,
|
||||||
|
)
|
||||||
|
self._history_current_period = [
|
||||||
|
HistoryState(state.state, state.last_changed.timestamp())
|
||||||
|
for state in states
|
||||||
|
]
|
||||||
|
|
||||||
|
def _state_changes_during_period(
|
||||||
self, start: datetime.datetime, end: datetime.datetime
|
self, start: datetime.datetime, end: datetime.datetime
|
||||||
) -> list[State]:
|
) -> list[State]:
|
||||||
return history.state_changes_during_period(
|
return history.state_changes_during_period(
|
||||||
@ -155,9 +178,9 @@ class HistoryStats:
|
|||||||
match_count = 1 if previous_state_matches else 0
|
match_count = 1 if previous_state_matches else 0
|
||||||
|
|
||||||
# Make calculations
|
# Make calculations
|
||||||
for item in self._history_current_period:
|
for history_state in self._history_current_period:
|
||||||
current_state_matches = item.state in self._entity_states
|
current_state_matches = history_state.state in self._entity_states
|
||||||
state_change_timestamp = item.last_changed.timestamp()
|
state_change_timestamp = history_state.last_changed
|
||||||
|
|
||||||
if previous_state_matches:
|
if previous_state_matches:
|
||||||
elapsed += state_change_timestamp - last_state_change_timestamp
|
elapsed += state_change_timestamp - last_state_change_timestamp
|
||||||
|
Loading…
x
Reference in New Issue
Block a user