From 0b9992260aecff76d8231e07823931966ab6d92c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 7 Jan 2024 17:35:28 -1000 Subject: [PATCH] Improve logbook context augment performance (#106926) Makes LazyEventPartialState a bit lazier since almost all the properties are never called. --- homeassistant/components/logbook/models.py | 46 +++++++++++-------- homeassistant/components/logbook/processor.py | 2 +- tests/components/logbook/test_models.py | 6 +++ 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/logbook/models.py b/homeassistant/components/logbook/models.py index 6939904f5201..04a2458237f0 100644 --- a/homeassistant/components/logbook/models.py +++ b/homeassistant/components/logbook/models.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -from typing import Any, cast +from typing import TYPE_CHECKING, Any, cast from sqlalchemy.engine.row import Row @@ -20,6 +20,11 @@ import homeassistant.util.dt as dt_util from homeassistant.util.json import json_loads from homeassistant.util.ulid import ulid_to_bytes +if TYPE_CHECKING: + from functools import cached_property +else: + from homeassistant.backports.functools import cached_property + @dataclass(slots=True) class LogbookConfig: @@ -35,16 +40,6 @@ class LogbookConfig: class LazyEventPartialState: """A lazy version of core Event with limited State joined in.""" - __slots__ = [ - "row", - "_event_data", - "_event_data_cache", - "event_type", - "entity_id", - "state", - "data", - ] - def __init__( self, row: Row | EventAsRow, @@ -54,9 +49,6 @@ class LazyEventPartialState: self.row = row self._event_data: dict[str, Any] | None = None self._event_data_cache = event_data_cache - self.event_type: str | None = self.row.event_type - self.entity_id: str | None = self.row.entity_id - self.state = self.row.state # We need to explicitly check for the row is EventAsRow as the unhappy path # to fetch row.data for Row is very expensive if type(row) is EventAsRow: # noqa: E721 @@ -64,7 +56,10 @@ class LazyEventPartialState: # json decode process as we already have the data self.data = row.data return - source = cast(str, self.row.event_data) + if TYPE_CHECKING: + source = cast(str, row.event_data) + else: + source = row.event_data if not source: self.data = {} elif event_data := self._event_data_cache.get(source): @@ -74,17 +69,32 @@ class LazyEventPartialState: dict[str, Any], json_loads(source) ) - @property + @cached_property + def event_type(self) -> str | None: + """Return the event type.""" + return self.row.event_type + + @cached_property + def entity_id(self) -> str | None: + """Return the entity id.""" + return self.row.entity_id + + @cached_property + def state(self) -> str | None: + """Return the state.""" + return self.row.state + + @cached_property def context_id(self) -> str | None: """Return the context id.""" return bytes_to_ulid_or_none(self.row.context_id_bin) - @property + @cached_property def context_user_id(self) -> str | None: """Return the context user id.""" return bytes_to_uuid_hex_or_none(self.row.context_user_id_bin) - @property + @cached_property def context_parent_id(self) -> str | None: """Return the context parent id.""" return bytes_to_ulid_or_none(self.row.context_parent_id_bin) diff --git a/homeassistant/components/logbook/processor.py b/homeassistant/components/logbook/processor.py index 671f8f8f1c2f..a36c887b5993 100644 --- a/homeassistant/components/logbook/processor.py +++ b/homeassistant/components/logbook/processor.py @@ -425,7 +425,7 @@ class EventCache: def get(self, row: EventAsRow | Row) -> LazyEventPartialState: """Get the event from the row.""" - if isinstance(row, EventAsRow): + if type(row) is EventAsRow: # noqa: E721 - this is never subclassed return LazyEventPartialState(row, self._event_data_cache) if event := self.event_cache.get(row): return event diff --git a/tests/components/logbook/test_models.py b/tests/components/logbook/test_models.py index 6f3c6bfefcb6..dcafd7e4765b 100644 --- a/tests/components/logbook/test_models.py +++ b/tests/components/logbook/test_models.py @@ -12,9 +12,15 @@ def test_lazy_event_partial_state_context(): context_user_id_bin=b"1234123412341234", context_parent_id_bin=b"4444444444444444", event_data={}, + event_type="event_type", + entity_id="entity_id", + state="state", ), {}, ) assert state.context_id == "1H68SK8C9J6CT32CHK6GRK4CSM" assert state.context_user_id == "31323334313233343132333431323334" assert state.context_parent_id == "1M6GT38D1M6GT38D1M6GT38D1M" + assert state.event_type == "event_type" + assert state.entity_id == "entity_id" + assert state.state == "state"