diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 665028b074d..7cd0ad56f79 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -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: diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 5c542405376..11233af1462 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -2075,3 +2075,18 @@ async def test_excluding_attributes_by_integration( expected = _state_with_context(hass, entity_id) expected.attributes = {"test_attr": 5} assert state.as_dict() == expected.as_dict() + + +async def test_lru_increases_with_many_entities( + recorder_mock: Recorder, hass: HomeAssistant +) -> None: + """Test that the recorder's internal LRU cache increases with many entities.""" + # We do not actually want to record 4096 entities so we mock the entity count + mock_entity_count = 4096 + with patch.object( + hass.states, "async_entity_ids_count", return_value=mock_entity_count + ): + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=10)) + await hass.async_block_till_done() + + assert recorder_mock._state_attributes_ids.get_size() == mock_entity_count * 2