Use cache to lookup event type ids in logbook (#91576)

noticed we can speed this up while looking at
https://github.com/home-assistant/core/issues/91514

Note: this will not fix that issue as there is more going on there
This commit is contained in:
J. Nick Koston 2023-04-17 17:40:03 -10:00 committed by GitHub
parent f96515b90a
commit 2530031454
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 55 additions and 35 deletions

View File

@ -14,6 +14,7 @@ from homeassistant.components.recorder import get_instance
from homeassistant.components.recorder.filters import Filters
from homeassistant.components.recorder.models import (
bytes_to_uuid_hex_or_none,
extract_event_type_ids,
extract_metadata_ids,
process_datetime_to_timestamp,
process_timestamp_to_utc_isoformat,
@ -153,17 +154,22 @@ class EventProcessor:
with session_scope(hass=self.hass, read_only=True) as session:
metadata_ids: list[int] | None = None
if self.entity_ids:
instance = get_instance(self.hass)
if self.entity_ids:
metadata_ids = extract_metadata_ids(
instance.states_meta_manager.get_many(
self.entity_ids, session, False
)
)
event_type_ids = tuple(
extract_event_type_ids(
instance.event_type_manager.get_many(self.event_types, session)
)
)
stmt = statement_for_request(
start_day,
end_day,
self.event_types,
event_type_ids,
self.entity_ids,
metadata_ids,
self.device_ids,

View File

@ -20,7 +20,7 @@ from .entities_and_devices import entities_devices_stmt
def statement_for_request(
start_day_dt: dt,
end_day_dt: dt,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
entity_ids: list[str] | None = None,
states_metadata_ids: Collection[int] | None = None,
device_ids: list[str] | None = None,
@ -37,7 +37,7 @@ def statement_for_request(
return all_stmt(
start_day,
end_day,
event_types,
event_type_ids,
filters,
context_id_bin,
)
@ -52,7 +52,7 @@ def statement_for_request(
return entities_devices_stmt(
start_day,
end_day,
event_types,
event_type_ids,
states_metadata_ids or [],
[json_dumps(entity_id) for entity_id in entity_ids],
[json_dumps(device_id) for device_id in device_ids],
@ -63,7 +63,7 @@ def statement_for_request(
return entities_stmt(
start_day,
end_day,
event_types,
event_type_ids,
states_metadata_ids or [],
[json_dumps(entity_id) for entity_id in entity_ids],
)
@ -73,6 +73,6 @@ def statement_for_request(
return devices_stmt(
start_day,
end_day,
event_types,
event_type_ids,
[json_dumps(device_id) for device_id in device_ids],
)

View File

@ -18,13 +18,13 @@ from .common import apply_states_filters, select_events_without_states, select_s
def all_stmt(
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
filters: Filters | None,
context_id_bin: bytes | None = None,
) -> StatementLambdaElement:
"""Generate a logbook query for all entities."""
stmt = lambda_stmt(
lambda: select_events_without_states(start_day, end_day, event_types)
lambda: select_events_without_states(start_day, end_day, event_type_ids)
)
if context_id_bin is not None:
stmt += lambda s: s.where(Events.context_id_bin == context_id_bin).union_all(

View File

@ -23,7 +23,6 @@ from homeassistant.components.recorder.db_schema import (
StatesMeta,
)
from homeassistant.components.recorder.filters import like_domain_matchers
from homeassistant.components.recorder.queries import select_event_type_ids
from ..const import ALWAYS_CONTINUOUS_DOMAINS, CONDITIONALLY_CONTINUOUS_DOMAINS
@ -112,13 +111,13 @@ NOT_CONTEXT_ONLY = literal(value=None, type_=sqlalchemy.String).label("context_o
def select_events_context_id_subquery(
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
) -> Select:
"""Generate the select for a context_id subquery."""
return (
select(Events.context_id_bin)
.where((Events.time_fired_ts > start_day) & (Events.time_fired_ts < end_day))
.where(Events.event_type_id.in_(select_event_type_ids(event_types)))
.where(Events.event_type_id.in_(event_type_ids))
.outerjoin(EventTypes, (Events.event_type_id == EventTypes.event_type_id))
.outerjoin(EventData, (Events.data_id == EventData.data_id))
)
@ -145,13 +144,13 @@ def select_states_context_only() -> Select:
def select_events_without_states(
start_day: float, end_day: float, event_types: tuple[str, ...]
start_day: float, end_day: float, event_type_ids: tuple[int, ...]
) -> Select:
"""Generate an events select that does not join states."""
return (
select(*EVENT_ROWS_NO_STATES, NOT_CONTEXT_ONLY)
.where((Events.time_fired_ts > start_day) & (Events.time_fired_ts < end_day))
.where(Events.event_type_id.in_(select_event_type_ids(event_types)))
.where(Events.event_type_id.in_(event_type_ids))
.outerjoin(EventTypes, (Events.event_type_id == EventTypes.event_type_id))
.outerjoin(EventData, (Events.data_id == EventData.data_id))
)

View File

@ -31,12 +31,12 @@ from .common import (
def _select_device_id_context_ids_sub_query(
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
json_quotable_device_ids: list[str],
) -> Select:
"""Generate a subquery to find context ids for multiple devices."""
inner = (
select_events_context_id_subquery(start_day, end_day, event_types)
select_events_context_id_subquery(start_day, end_day, event_type_ids)
.where(apply_event_device_id_matchers(json_quotable_device_ids))
.subquery()
)
@ -47,14 +47,14 @@ def _apply_devices_context_union(
sel: Select,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
json_quotable_device_ids: list[str],
) -> CompoundSelect:
"""Generate a CTE to find the device context ids and a query to find linked row."""
devices_cte: CTE = _select_device_id_context_ids_sub_query(
start_day,
end_day,
event_types,
event_type_ids,
json_quotable_device_ids,
).cte()
return sel.union_all(
@ -77,18 +77,18 @@ def _apply_devices_context_union(
def devices_stmt(
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
json_quotable_device_ids: list[str],
) -> StatementLambdaElement:
"""Generate a logbook query for multiple devices."""
stmt = lambda_stmt(
lambda: _apply_devices_context_union(
select_events_without_states(start_day, end_day, event_types).where(
select_events_without_states(start_day, end_day, event_type_ids).where(
apply_event_device_id_matchers(json_quotable_device_ids)
),
start_day,
end_day,
event_types,
event_type_ids,
json_quotable_device_ids,
).order_by(Events.time_fired_ts)
)

View File

@ -35,13 +35,13 @@ from .common import (
def _select_entities_context_ids_sub_query(
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
states_metadata_ids: Collection[int],
json_quoted_entity_ids: list[str],
) -> Select:
"""Generate a subquery to find context ids for multiple entities."""
union = union_all(
select_events_context_id_subquery(start_day, end_day, event_types).where(
select_events_context_id_subquery(start_day, end_day, event_type_ids).where(
apply_event_entity_id_matchers(json_quoted_entity_ids)
),
apply_entities_hints(select(States.context_id_bin))
@ -57,7 +57,7 @@ def _apply_entities_context_union(
sel: Select,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
states_metadata_ids: Collection[int],
json_quoted_entity_ids: list[str],
) -> CompoundSelect:
@ -65,7 +65,7 @@ def _apply_entities_context_union(
entities_cte: CTE = _select_entities_context_ids_sub_query(
start_day,
end_day,
event_types,
event_type_ids,
states_metadata_ids,
json_quoted_entity_ids,
).cte()
@ -95,19 +95,19 @@ def _apply_entities_context_union(
def entities_stmt(
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
states_metadata_ids: Collection[int],
json_quoted_entity_ids: list[str],
) -> StatementLambdaElement:
"""Generate a logbook query for multiple entities."""
return lambda_stmt(
lambda: _apply_entities_context_union(
select_events_without_states(start_day, end_day, event_types).where(
select_events_without_states(start_day, end_day, event_type_ids).where(
apply_event_entity_id_matchers(json_quoted_entity_ids)
),
start_day,
end_day,
event_types,
event_type_ids,
states_metadata_ids,
json_quoted_entity_ids,
).order_by(Events.time_fired_ts)

View File

@ -35,14 +35,14 @@ from .entities import (
def _select_entities_device_id_context_ids_sub_query(
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
states_metadata_ids: Collection[int],
json_quoted_entity_ids: list[str],
json_quoted_device_ids: list[str],
) -> Select:
"""Generate a subquery to find context ids for multiple entities and multiple devices."""
union = union_all(
select_events_context_id_subquery(start_day, end_day, event_types).where(
select_events_context_id_subquery(start_day, end_day, event_type_ids).where(
_apply_event_entity_id_device_id_matchers(
json_quoted_entity_ids, json_quoted_device_ids
)
@ -60,7 +60,7 @@ def _apply_entities_devices_context_union(
sel: Select,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
states_metadata_ids: Collection[int],
json_quoted_entity_ids: list[str],
json_quoted_device_ids: list[str],
@ -68,7 +68,7 @@ def _apply_entities_devices_context_union(
devices_entities_cte: CTE = _select_entities_device_id_context_ids_sub_query(
start_day,
end_day,
event_types,
event_type_ids,
states_metadata_ids,
json_quoted_entity_ids,
json_quoted_device_ids,
@ -103,7 +103,7 @@ def _apply_entities_devices_context_union(
def entities_devices_stmt(
start_day: float,
end_day: float,
event_types: tuple[str, ...],
event_type_ids: tuple[int, ...],
states_metadata_ids: Collection[int],
json_quoted_entity_ids: list[str],
json_quoted_device_ids: list[str],
@ -111,14 +111,14 @@ def entities_devices_stmt(
"""Generate a logbook query for multiple entities."""
stmt = lambda_stmt(
lambda: _apply_entities_devices_context_union(
select_events_without_states(start_day, end_day, event_types).where(
select_events_without_states(start_day, end_day, event_type_ids).where(
_apply_event_entity_id_device_id_matchers(
json_quoted_entity_ids, json_quoted_device_ids
)
),
start_day,
end_day,
event_types,
event_type_ids,
states_metadata_ids,
json_quoted_entity_ids,
json_quoted_device_ids,

View File

@ -8,6 +8,7 @@ from .context import (
uuid_hex_to_bytes_or_none,
)
from .database import DatabaseEngine, DatabaseOptimizer, UnsupportedDialect
from .event import extract_event_type_ids
from .state import LazyState, extract_metadata_ids, row_to_compressed_state
from .statistics import (
CalendarStatisticPeriod,
@ -43,6 +44,7 @@ __all__ = [
"bytes_to_ulid_or_none",
"bytes_to_uuid_hex_or_none",
"datetime_to_timestamp_or_none",
"extract_event_type_ids",
"extract_metadata_ids",
"process_datetime_to_timestamp",
"process_timestamp",

View File

@ -0,0 +1,13 @@
"""Models events in for Recorder."""
from __future__ import annotations
def extract_event_type_ids(
event_type_to_event_type_id: dict[str, int | None],
) -> list[int]:
"""Extract event_type ids from event_type_to_event_type_id."""
return [
event_type_id
for event_type_id in event_type_to_event_type_id.values()
if event_type_id is not None
]