Reduce overhead to compile statistics (#106927)

* Reduce overhead to compile statistics

statistics uses LazyState for compatibility with State when pulling
data from the database.

After the previous round of refactoring to modern history, the setters
are never called and can be removed.

* reduce
This commit is contained in:
J. Nick Koston 2024-01-07 17:36:49 -10:00 committed by GitHub
parent d04e2d56da
commit acf78664e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 26 additions and 71 deletions

View file

@ -3,7 +3,7 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
import logging import logging
from typing import Any from typing import TYPE_CHECKING, Any
from sqlalchemy.engine.row import Row from sqlalchemy.engine.row import Row
@ -17,10 +17,16 @@ from homeassistant.core import Context, State
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from .state_attributes import decode_attributes_from_source from .state_attributes import decode_attributes_from_source
from .time import process_timestamp
if TYPE_CHECKING:
from functools import cached_property
else:
from homeassistant.backports.functools import cached_property
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
EMPTY_CONTEXT = Context(id=None)
def extract_metadata_ids( def extract_metadata_ids(
entity_id_to_metadata_id: dict[str, int | None], entity_id_to_metadata_id: dict[str, int | None],
@ -36,15 +42,6 @@ def extract_metadata_ids(
class LazyState(State): class LazyState(State):
"""A lazy version of core State after schema 31.""" """A lazy version of core State after schema 31."""
__slots__ = [
"_row",
"_attributes",
"_last_changed_ts",
"_last_updated_ts",
"_context",
"attr_cache",
]
def __init__( # pylint: disable=super-init-not-called def __init__( # pylint: disable=super-init-not-called
self, self,
row: Row, row: Row,
@ -61,61 +58,35 @@ class LazyState(State):
self.state = state or "" self.state = state or ""
self._attributes: dict[str, Any] | None = None self._attributes: dict[str, Any] | None = None
self._last_updated_ts: float | None = last_updated_ts or start_time_ts self._last_updated_ts: float | None = last_updated_ts or start_time_ts
self._last_changed_ts: float | None = None
self._context: Context | None = None
self.attr_cache = attr_cache self.attr_cache = attr_cache
self.context = EMPTY_CONTEXT
@property # type: ignore[override] @cached_property # type: ignore[override]
def attributes(self) -> dict[str, Any]: def attributes(self) -> dict[str, Any]:
"""State attributes.""" """State attributes."""
if self._attributes is None: return decode_attributes_from_source(
self._attributes = decode_attributes_from_source(
getattr(self._row, "attributes", None), self.attr_cache getattr(self._row, "attributes", None), self.attr_cache
) )
return self._attributes
@attributes.setter @cached_property
def attributes(self, value: dict[str, Any]) -> None: def _last_changed_ts(self) -> float | None:
"""Set attributes.""" """Last changed timestamp."""
self._attributes = value return getattr(self._row, "last_changed_ts", None)
@property @cached_property
def context(self) -> Context: def last_changed(self) -> datetime: # type: ignore[override]
"""State context."""
if self._context is None:
self._context = Context(id=None)
return self._context
@context.setter
def context(self, value: Context) -> None:
"""Set context."""
self._context = value
@property
def last_changed(self) -> datetime:
"""Last changed datetime.""" """Last changed datetime."""
if self._last_changed_ts is None: return dt_util.utc_from_timestamp(
self._last_changed_ts = ( self._last_changed_ts or self._last_updated_ts
getattr(self._row, "last_changed_ts", None) or self._last_updated_ts
) )
return dt_util.utc_from_timestamp(self._last_changed_ts)
@last_changed.setter @cached_property
def last_changed(self, value: datetime) -> None: def last_updated(self) -> datetime: # type: ignore[override]
"""Set last changed datetime."""
self._last_changed_ts = process_timestamp(value).timestamp()
@property
def last_updated(self) -> datetime:
"""Last updated datetime.""" """Last updated datetime."""
if TYPE_CHECKING:
assert self._last_updated_ts is not None assert self._last_updated_ts is not None
return dt_util.utc_from_timestamp(self._last_updated_ts) return dt_util.utc_from_timestamp(self._last_updated_ts)
@last_updated.setter
def last_updated(self, value: datetime) -> None:
"""Set last updated datetime."""
self._last_updated_ts = process_timestamp(value).timestamp()
def as_dict(self) -> dict[str, Any]: # type: ignore[override] def as_dict(self) -> dict[str, Any]: # type: ignore[override]
"""Return a dict representation of the LazyState. """Return a dict representation of the LazyState.

View file

@ -352,22 +352,6 @@ async def test_lazy_state_handles_same_last_updated_and_last_changed(
"last_updated": "2021-06-12T03:04:01.000323+00:00", "last_updated": "2021-06-12T03:04:01.000323+00:00",
"state": "off", "state": "off",
} }
lstate.last_updated = datetime(2020, 6, 12, 3, 4, 1, 323, tzinfo=dt_util.UTC)
assert lstate.as_dict() == {
"attributes": {"shared": True},
"entity_id": "sensor.valid",
"last_changed": "2021-06-12T03:04:01.000323+00:00",
"last_updated": "2020-06-12T03:04:01.000323+00:00",
"state": "off",
}
lstate.last_changed = datetime(2020, 6, 12, 3, 4, 1, 323, tzinfo=dt_util.UTC)
assert lstate.as_dict() == {
"attributes": {"shared": True},
"entity_id": "sensor.valid",
"last_changed": "2020-06-12T03:04:01.000323+00:00",
"last_updated": "2020-06-12T03:04:01.000323+00:00",
"state": "off",
}
@pytest.mark.parametrize( @pytest.mark.parametrize(