Adjust recorder LRU cache to align with the number of entities (#88350)

* Adjust size of recorder LRU based on number of entities

If there are a large number of entities the cache would
get thrashed as there were more state attributes being
recorded than the size of the cache. This meant we had
to go back to the database to do lookups frequently when
an instance has more than 2048 entities that change
frequently

* add a test

* do not actually record 4096 states

* patch target

* patch target
This commit is contained in:
J. Nick Koston 2023-02-18 23:02:36 -06:00 committed by GitHub
parent e799f24853
commit 9df00bd285
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 2 deletions

View file

@ -411,6 +411,7 @@ class Recorder(threading.Thread):
@callback
def _async_hass_started(self, hass: HomeAssistant) -> None:
"""Notify that hass has started."""
self.async_adjust_lru()
self._hass_started.set_result(None)
@callback
@ -475,7 +476,25 @@ class Recorder(threading.Thread):
self.queue_task(PerodicCleanupTask())
@callback
def async_periodic_statistics(self, now: datetime) -> None:
def _async_five_minute_tasks(self, now: datetime) -> None:
"""Run tasks every five minutes."""
self.async_adjust_lru()
self.async_periodic_statistics()
@callback
def async_adjust_lru(self) -> None:
"""Trigger the LRU adjustment.
If the number of entities has increased, increase the size of the LRU
cache to avoid thrashing.
"""
current_size = self._state_attributes_ids.get_size()
new_size = self.hass.states.async_entity_ids_count() * 2
if new_size > current_size:
self._state_attributes_ids.set_size(new_size)
@callback
def async_periodic_statistics(self) -> None:
"""Trigger the statistics run.
Short term statistics run every 5 minutes
@ -570,7 +589,7 @@ class Recorder(threading.Thread):
# Compile short term statistics every 5 minutes
self._periodic_listener = async_track_utc_time_change(
self.hass, self.async_periodic_statistics, minute=range(0, 60, 5), second=10
self.hass, self._async_five_minute_tasks, minute=range(0, 60, 5), second=10
)
async def _async_wait_for_started(self) -> object | None: