mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 08:47:10 +00:00
Improve template ratelimit performance (#41741)
This commit is contained in:
parent
1f07a4eba0
commit
21cc23244d
@ -758,15 +758,15 @@ class _TrackTemplateResultInfo:
|
|||||||
for track_template_ in self._track_templates:
|
for track_template_ in self._track_templates:
|
||||||
template = track_template_.template
|
template = track_template_.template
|
||||||
variables = track_template_.variables
|
variables = track_template_.variables
|
||||||
|
self._info[template] = info = template.async_render_to_info(variables)
|
||||||
|
|
||||||
self._info[template] = template.async_render_to_info(variables)
|
if info.exception:
|
||||||
if self._info[template].exception:
|
|
||||||
if raise_on_template_error:
|
if raise_on_template_error:
|
||||||
raise self._info[template].exception
|
raise info.exception
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Error while processing template: %s",
|
"Error while processing template: %s",
|
||||||
track_template_.template,
|
track_template_.template,
|
||||||
exc_info=self._info[template].exception,
|
exc_info=info.exception,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._track_state_changes = async_track_state_change_filtered(
|
self._track_state_changes = async_track_state_change_filtered(
|
||||||
@ -817,9 +817,7 @@ class _TrackTemplateResultInfo:
|
|||||||
if event:
|
if event:
|
||||||
info = self._info[template]
|
info = self._info[template]
|
||||||
|
|
||||||
if not self._rate_limit.async_has_timer(
|
if not _event_triggers_rerender(event, info):
|
||||||
template
|
|
||||||
) and not _event_triggers_rerender(event, info):
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self._rate_limit.async_schedule_action(
|
if self._rate_limit.async_schedule_action(
|
||||||
@ -828,6 +826,8 @@ class _TrackTemplateResultInfo:
|
|||||||
now,
|
now,
|
||||||
self._refresh,
|
self._refresh,
|
||||||
event,
|
event,
|
||||||
|
(track_template_,),
|
||||||
|
True,
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -838,10 +838,12 @@ class _TrackTemplateResultInfo:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self._rate_limit.async_triggered(template, now)
|
self._rate_limit.async_triggered(template, now)
|
||||||
self._info[template] = template.async_render_to_info(track_template_.variables)
|
self._info[template] = info = template.async_render_to_info(
|
||||||
|
track_template_.variables
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result: Union[str, TemplateError] = self._info[template].result()
|
result: Union[str, TemplateError] = info.result()
|
||||||
except TemplateError as ex:
|
except TemplateError as ex:
|
||||||
result = ex
|
result = ex
|
||||||
|
|
||||||
@ -857,12 +859,29 @@ class _TrackTemplateResultInfo:
|
|||||||
return TrackTemplateResult(template, last_result, result)
|
return TrackTemplateResult(template, last_result, result)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _refresh(self, event: Optional[Event]) -> None:
|
def _refresh(
|
||||||
|
self,
|
||||||
|
event: Optional[Event],
|
||||||
|
track_templates: Optional[Iterable[TrackTemplate]] = None,
|
||||||
|
replayed: Optional[bool] = False,
|
||||||
|
) -> None:
|
||||||
|
"""Refresh the template.
|
||||||
|
|
||||||
|
The event is the state_changed event that caused the refresh
|
||||||
|
to be considered.
|
||||||
|
|
||||||
|
track_templates is an optional list of TrackTemplate objects
|
||||||
|
to refresh. If not provided, all tracked templates will be
|
||||||
|
considered.
|
||||||
|
|
||||||
|
replayed is True if the event is being replayed because the
|
||||||
|
rate limit was hit.
|
||||||
|
"""
|
||||||
updates = []
|
updates = []
|
||||||
info_changed = False
|
info_changed = False
|
||||||
now = dt_util.utcnow()
|
now = event.time_fired if not replayed and event else dt_util.utcnow()
|
||||||
|
|
||||||
for track_template_ in self._track_templates:
|
for track_template_ in track_templates or self._track_templates:
|
||||||
update = self._render_template_if_ready(track_template_, now, event)
|
update = self._render_template_if_ready(track_template_, now, event)
|
||||||
if not update:
|
if not update:
|
||||||
continue
|
continue
|
||||||
|
@ -26,6 +26,8 @@ class KeyedRateLimit:
|
|||||||
@callback
|
@callback
|
||||||
def async_has_timer(self, key: Hashable) -> bool:
|
def async_has_timer(self, key: Hashable) -> bool:
|
||||||
"""Check if a rate limit timer is running."""
|
"""Check if a rate limit timer is running."""
|
||||||
|
if not self._rate_limit_timers:
|
||||||
|
return False
|
||||||
return key in self._rate_limit_timers
|
return key in self._rate_limit_timers
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@ -37,7 +39,7 @@ class KeyedRateLimit:
|
|||||||
@callback
|
@callback
|
||||||
def async_cancel_timer(self, key: Hashable) -> None:
|
def async_cancel_timer(self, key: Hashable) -> None:
|
||||||
"""Cancel a rate limit time that will call the action."""
|
"""Cancel a rate limit time that will call the action."""
|
||||||
if not self.async_has_timer(key):
|
if not self._rate_limit_timers or not self.async_has_timer(key):
|
||||||
return
|
return
|
||||||
|
|
||||||
self._rate_limit_timers.pop(key).cancel()
|
self._rate_limit_timers.pop(key).cancel()
|
||||||
@ -71,10 +73,14 @@ class KeyedRateLimit:
|
|||||||
|
|
||||||
Return None
|
Return None
|
||||||
"""
|
"""
|
||||||
if rate_limit is None or key not in self._last_triggered:
|
if rate_limit is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
next_call_time = self._last_triggered[key] + rate_limit
|
last_triggered = self._last_triggered.get(key)
|
||||||
|
if not last_triggered:
|
||||||
|
return None
|
||||||
|
|
||||||
|
next_call_time = last_triggered + rate_limit
|
||||||
|
|
||||||
if next_call_time <= now:
|
if next_call_time <= now:
|
||||||
self.async_cancel_timer(key)
|
self.async_cancel_timer(key)
|
||||||
|
@ -213,6 +213,10 @@ class RenderInfo:
|
|||||||
split_entity_id(entity_id)[0] in self.domains or entity_id in self.entities
|
split_entity_id(entity_id)[0] in self.domains or entity_id in self.entities
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _filter_entities(self, entity_id: str) -> bool:
|
||||||
|
"""Template should re-render if the entity state changes when we match specific entities."""
|
||||||
|
return entity_id in self.entities
|
||||||
|
|
||||||
def _filter_lifecycle_domains(self, entity_id: str) -> bool:
|
def _filter_lifecycle_domains(self, entity_id: str) -> bool:
|
||||||
"""Template should re-render if the entity is added or removed with domains watched."""
|
"""Template should re-render if the entity is added or removed with domains watched."""
|
||||||
return split_entity_id(entity_id)[0] in self.domains_lifecycle
|
return split_entity_id(entity_id)[0] in self.domains_lifecycle
|
||||||
@ -255,8 +259,10 @@ class RenderInfo:
|
|||||||
if self.all_states:
|
if self.all_states:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.entities or self.domains:
|
if self.domains:
|
||||||
self.filter = self._filter_domains_and_entities
|
self.filter = self._filter_domains_and_entities
|
||||||
|
elif self.entities:
|
||||||
|
self.filter = self._filter_entities
|
||||||
else:
|
else:
|
||||||
self.filter = _false
|
self.filter = _false
|
||||||
|
|
||||||
@ -264,6 +270,15 @@ class RenderInfo:
|
|||||||
class Template:
|
class Template:
|
||||||
"""Class to hold a template and manage caching and rendering."""
|
"""Class to hold a template and manage caching and rendering."""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
"__weakref__",
|
||||||
|
"template",
|
||||||
|
"hass",
|
||||||
|
"is_static",
|
||||||
|
"_compiled_code",
|
||||||
|
"_compiled",
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, template, hass=None):
|
def __init__(self, template, hass=None):
|
||||||
"""Instantiate a template."""
|
"""Instantiate a template."""
|
||||||
if not isinstance(template, str):
|
if not isinstance(template, str):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user