1
mirror of https://github.com/home-assistant/core synced 2024-07-15 09:42:11 +02:00

Improve logbook context augment performance (#106926)

Makes LazyEventPartialState a bit lazier since almost all the
properties are never called.
This commit is contained in:
J. Nick Koston 2024-01-07 17:35:28 -10:00 committed by GitHub
parent d8c6534aff
commit 0b9992260a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 19 deletions

View File

@ -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)

View File

@ -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

View File

@ -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"