Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
b9bb35c
Avoid the overhead of storing database timestamps as strings
bdraco Dec 30, 2022
bad8f17
Avoid the overhead of storing database timestamps as strings
bdraco Dec 30, 2022
14b9e9c
Avoid the overhead of storing database timestamps as strings
bdraco Dec 30, 2022
8e77224
Avoid the overhead of storing database timestamps as strings
bdraco Dec 30, 2022
830025b
Avoid the overhead of storing database timestamps as strings
bdraco Dec 30, 2022
224c0ee
fixes
bdraco Dec 30, 2022
1caa020
cleanup
bdraco Dec 30, 2022
2ca2414
more fixes
bdraco Dec 30, 2022
611000f
updated
bdraco Dec 30, 2022
a8ed817
fix a few more
bdraco Dec 30, 2022
a265b59
Merge remote-tracking branch 'upstream/dev' into time_as_float
bdraco Dec 30, 2022
378e05f
tweak
bdraco Dec 31, 2022
8a65d42
fix schema
bdraco Dec 31, 2022
cecc80b
fixes
bdraco Dec 31, 2022
78f8607
Merge branch 'dev' into time_as_float
bdraco Dec 31, 2022
2a73ca4
tweak
bdraco Dec 31, 2022
fd7b1fd
fix migration
bdraco Dec 31, 2022
5c12054
fix model tests
bdraco Dec 31, 2022
d9a1f5f
use the same as stats
bdraco Dec 31, 2022
20a954b
Merge branch 'dev' into time_as_float
bdraco Dec 31, 2022
919263c
fix
bdraco Jan 1, 2023
e4d4f8e
reduce
bdraco Jan 1, 2023
d127cfc
add year 2038 test
bdraco Jan 1, 2023
bf1ee08
add year 2038 test
bdraco Jan 1, 2023
d5925ae
implement migration
bdraco Jan 1, 2023
31d9cc5
add time migration test
bdraco Jan 1, 2023
75c9c3a
Update homeassistant/components/recorder/migration.py
bdraco Jan 1, 2023
a8a7e9b
Update homeassistant/components/recorder/migration.py
bdraco Jan 1, 2023
ea6602d
Fix failing HomeKit Controller diagnostics tests
bdraco Jan 1, 2023
7ccc7ff
Merge branch 'hkc_tests' into time_as_float
bdraco Jan 1, 2023
6e169d4
Merge remote-tracking branch 'origin/time_as_float' into time_as_float
bdraco Jan 1, 2023
f04f424
Merge branch 'dev' into time_as_float
bdraco Jan 1, 2023
c60ab5c
fix v23 migration tests
bdraco Jan 1, 2023
d2e1a50
Merge remote-tracking branch 'origin/time_as_float' into time_as_float
bdraco Jan 1, 2023
7d5313a
tests for legacy schema
bdraco Jan 1, 2023
985dd08
Merge branch 'dev' into time_as_float
bdraco Jan 1, 2023
ff088a3
Apply suggestions from code review
bdraco Jan 1, 2023
25b1289
lint
bdraco Jan 1, 2023
a1e972f
fix missing ,s
bdraco Jan 1, 2023
221c5bf
postgres fixes
bdraco Jan 1, 2023
b792769
reduce memory pressure and ensure we switch to the new history path a…
bdraco Jan 1, 2023
4079504
fix mysql
bdraco Jan 1, 2023
7c8e991
preen
bdraco Jan 1, 2023
c4bc74e
add tests for history apis still work during migration
bdraco Jan 1, 2023
89e0c94
Merge branch 'dev' into time_as_float
bdraco Jan 1, 2023
acebe89
small cleanups
bdraco Jan 2, 2023
c788df5
Merge remote-tracking branch 'origin/time_as_float' into time_as_float
bdraco Jan 2, 2023
199ed4d
Merge branch 'dev' into time_as_float
bdraco Jan 2, 2023
d7c9576
Merge branch 'dev' into time_as_float
bdraco Jan 2, 2023
294f431
more cover
bdraco Jan 2, 2023
0cb4904
Merge remote-tracking branch 'origin/time_as_float' into time_as_float
bdraco Jan 2, 2023
dafd2c5
Merge branch 'dev' into time_as_float
bdraco Jan 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions homeassistant/components/logbook/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime as dt
import json
from typing import Any, cast

from sqlalchemy.engine.row import Row

from homeassistant.const import ATTR_ICON, EVENT_STATE_CHANGED
from homeassistant.core import Context, Event, State, callback
import homeassistant.util.dt as dt_util


class LazyEventPartialState:
Expand Down Expand Up @@ -66,7 +66,7 @@ class EventAsRow:
data: dict[str, Any]
context: Context
context_id: str
time_fired: dt
time_fired_ts: float
state_id: int
event_data: str | None = None
old_format_icon: None = None
Expand All @@ -92,7 +92,7 @@ def async_event_to_row(event: Event) -> EventAsRow | None:
context_id=event.context.id,
context_user_id=event.context.user_id,
context_parent_id=event.context.parent_id,
time_fired=event.time_fired,
time_fired_ts=dt_util.utc_to_timestamp(event.time_fired),
state_id=hash(event),
)
# States are prefiltered so we never get states
Expand All @@ -107,7 +107,7 @@ def async_event_to_row(event: Event) -> EventAsRow | None:
context_id=new_state.context.id,
context_user_id=new_state.context.user_id,
context_parent_id=new_state.context.parent_id,
time_fired=new_state.last_updated,
time_fired_ts=dt_util.utc_to_timestamp(new_state.last_updated),
state_id=hash(event),
icon=new_state.attributes.get(ATTR_ICON),
)
6 changes: 4 additions & 2 deletions homeassistant/components/logbook/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,14 @@ def _rows_match(row: Row | EventAsRow, other_row: Row | EventAsRow) -> bool:

def _row_time_fired_isoformat(row: Row | EventAsRow) -> str:
"""Convert the row timed_fired to isoformat."""
return process_timestamp_to_utc_isoformat(row.time_fired or dt_util.utcnow())
return process_timestamp_to_utc_isoformat(
dt_util.utc_from_timestamp(row.time_fired_ts) or dt_util.utcnow()
)


def _row_time_fired_timestamp(row: Row | EventAsRow) -> float:
"""Convert the row timed_fired to timestamp."""
return process_datetime_to_timestamp(row.time_fired or dt_util.utcnow())
return row.time_fired_ts or process_datetime_to_timestamp(dt_util.utcnow())


class EntityNameCache:
Expand Down
8 changes: 5 additions & 3 deletions homeassistant/components/logbook/queries/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from homeassistant.components.recorder.filters import Filters
from homeassistant.helpers.json import json_dumps
from homeassistant.util import dt as dt_util

from .all import all_stmt
from .devices import devices_stmt
Expand All @@ -15,16 +16,17 @@


def statement_for_request(
start_day: dt,
end_day: dt,
start_day_dt: dt,
end_day_dt: dt,
event_types: tuple[str, ...],
entity_ids: list[str] | None = None,
device_ids: list[str] | None = None,
filters: Filters | None = None,
context_id: str | None = None,
) -> StatementLambdaElement:
"""Generate the logbook statement for a logbook request."""

start_day = dt_util.utc_to_timestamp(start_day_dt)
end_day = dt_util.utc_to_timestamp(end_day_dt)
# No entities: logbook sends everything for the timeframe
# limited by the context_id and the yaml configured filter
if not entity_ids and not device_ids:
Expand Down
18 changes: 9 additions & 9 deletions homeassistant/components/logbook/queries/all.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
"""All queries for logbook."""
from __future__ import annotations

from datetime import datetime as dt

from sqlalchemy import lambda_stmt
from sqlalchemy.orm import Query
from sqlalchemy.sql.elements import ClauseList
from sqlalchemy.sql.lambdas import StatementLambdaElement

from homeassistant.components.recorder.db_schema import (
LAST_UPDATED_INDEX,
LAST_UPDATED_INDEX_TS,
Events,
States,
)
Expand All @@ -23,8 +21,8 @@


def all_stmt(
start_day: dt,
end_day: dt,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
states_entity_filter: ClauseList | None = None,
events_entity_filter: ClauseList | None = None,
Expand Down Expand Up @@ -53,22 +51,24 @@ def all_stmt(
else:
stmt += lambda s: s.union_all(_states_query_for_all(start_day, end_day))

stmt += lambda s: s.order_by(Events.time_fired)
stmt += lambda s: s.order_by(Events.time_fired_ts)
return stmt


def _states_query_for_all(start_day: dt, end_day: dt) -> Query:
def _states_query_for_all(start_day: float, end_day: float) -> Query:
return apply_states_filters(_apply_all_hints(select_states()), start_day, end_day)


def _apply_all_hints(query: Query) -> Query:
"""Force mysql to use the right index on large selects."""
return query.with_hint(
States, f"FORCE INDEX ({LAST_UPDATED_INDEX})", dialect_name="mysql"
States, f"FORCE INDEX ({LAST_UPDATED_INDEX_TS})", dialect_name="mysql"
)


def _states_query_for_context_id(start_day: dt, end_day: dt, context_id: str) -> Query:
def _states_query_for_context_id(
start_day: float, end_day: float, context_id: str
) -> Query:
return apply_states_filters(select_states(), start_day, end_day).where(
States.context_id == context_id
)
32 changes: 16 additions & 16 deletions homeassistant/components/logbook/queries/common.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"""Queries for logbook."""
from __future__ import annotations

from datetime import datetime as dt

import sqlalchemy
from sqlalchemy import select
from sqlalchemy.orm import Query
Expand Down Expand Up @@ -47,7 +45,7 @@
Events.event_id.label("event_id"),
Events.event_type.label("event_type"),
Events.event_data.label("event_data"),
Events.time_fired.label("time_fired"),
Events.time_fired_ts.label("time_fired_ts"),
Events.context_id.label("context_id"),
Events.context_user_id.label("context_user_id"),
Events.context_parent_id.label("context_parent_id"),
Expand Down Expand Up @@ -79,7 +77,7 @@
"event_type"
),
literal(value=None, type_=sqlalchemy.Text).label("event_data"),
States.last_updated.label("time_fired"),
States.last_updated_ts.label("time_fired_ts"),
States.context_id.label("context_id"),
States.context_user_id.label("context_user_id"),
States.context_parent_id.label("context_parent_id"),
Expand Down Expand Up @@ -108,14 +106,14 @@


def select_events_context_id_subquery(
start_day: dt,
end_day: dt,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
) -> Select:
"""Generate the select for a context_id subquery."""
return (
select(Events.context_id)
.where((Events.time_fired > start_day) & (Events.time_fired < end_day))
.where((Events.time_fired_ts > start_day) & (Events.time_fired_ts < end_day))
.where(Events.event_type.in_(event_types))
.outerjoin(EventData, (Events.data_id == EventData.data_id))
)
Expand All @@ -142,12 +140,12 @@ def select_states_context_only() -> Select:


def select_events_without_states(
start_day: dt, end_day: dt, event_types: tuple[str, ...]
start_day: float, end_day: float, event_types: tuple[str, ...]
) -> Select:
"""Generate an events select that does not join states."""
return (
select(*EVENT_ROWS_NO_STATES, NOT_CONTEXT_ONLY)
.where((Events.time_fired > start_day) & (Events.time_fired < end_day))
.where((Events.time_fired_ts > start_day) & (Events.time_fired_ts < end_day))
.where(Events.event_type.in_(event_types))
.outerjoin(EventData, (Events.data_id == EventData.data_id))
)
Expand All @@ -163,7 +161,7 @@ def select_states() -> Select:


def legacy_select_events_context_id(
start_day: dt, end_day: dt, context_id: str
start_day: float, end_day: float, context_id: str
) -> Select:
"""Generate a legacy events context id select that also joins states."""
# This can be removed once we no longer have event_ids in the states table
Expand All @@ -176,33 +174,35 @@ def legacy_select_events_context_id(
)
.outerjoin(States, (Events.event_id == States.event_id))
.where(
(States.last_updated == States.last_changed) | States.last_changed.is_(None)
(States.last_updated_ts == States.last_changed_ts)
| States.last_changed_ts.is_(None)
)
.where(_not_continuous_entity_matcher())
.outerjoin(
StateAttributes, (States.attributes_id == StateAttributes.attributes_id)
)
.where((Events.time_fired > start_day) & (Events.time_fired < end_day))
.where((Events.time_fired_ts > start_day) & (Events.time_fired_ts < end_day))
.where(Events.context_id == context_id)
)


def apply_states_filters(query: Query, start_day: dt, end_day: dt) -> Query:
def apply_states_filters(query: Query, start_day: float, end_day: float) -> Query:
"""Filter states by time range.

Filters states that do not have an old state or new state (added / removed)
Filters states that are in a continuous domain with a UOM.
Filters states that do not have matching last_updated and last_changed.
Filters states that do not have matching last_updated_ts and last_changed_ts.
"""
return (
query.filter(
(States.last_updated > start_day) & (States.last_updated < end_day)
(States.last_updated_ts > start_day) & (States.last_updated_ts < end_day)
)
.outerjoin(OLD_STATE, (States.old_state_id == OLD_STATE.state_id))
.where(_missing_state_matcher())
.where(_not_continuous_entity_matcher())
.where(
(States.last_updated == States.last_changed) | States.last_changed.is_(None)
(States.last_updated_ts == States.last_changed_ts)
| States.last_changed_ts.is_(None)
)
.outerjoin(
StateAttributes, (States.attributes_id == StateAttributes.attributes_id)
Expand Down
15 changes: 7 additions & 8 deletions homeassistant/components/logbook/queries/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from __future__ import annotations

from collections.abc import Iterable
from datetime import datetime as dt

import sqlalchemy
from sqlalchemy import lambda_stmt, select
Expand All @@ -29,8 +28,8 @@


def _select_device_id_context_ids_sub_query(
start_day: dt,
end_day: dt,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
json_quotable_device_ids: list[str],
) -> CompoundSelect:
Expand All @@ -43,8 +42,8 @@ def _select_device_id_context_ids_sub_query(

def _apply_devices_context_union(
query: Query,
start_day: dt,
end_day: dt,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
json_quotable_device_ids: list[str],
) -> CompoundSelect:
Expand All @@ -70,8 +69,8 @@ def _apply_devices_context_union(


def devices_stmt(
start_day: dt,
end_day: dt,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
json_quotable_device_ids: list[str],
) -> StatementLambdaElement:
Expand All @@ -85,7 +84,7 @@ def devices_stmt(
end_day,
event_types,
json_quotable_device_ids,
).order_by(Events.time_fired)
).order_by(Events.time_fired_ts)
)
return stmt

Expand Down
25 changes: 13 additions & 12 deletions homeassistant/components/logbook/queries/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from __future__ import annotations

from collections.abc import Iterable
from datetime import datetime as dt

import sqlalchemy
from sqlalchemy import lambda_stmt, select, union_all
Expand All @@ -12,7 +11,7 @@

from homeassistant.components.recorder.db_schema import (
ENTITY_ID_IN_EVENT,
ENTITY_ID_LAST_UPDATED_INDEX,
ENTITY_ID_LAST_UPDATED_INDEX_TS,
OLD_ENTITY_ID_IN_EVENT,
EventData,
Events,
Expand All @@ -32,8 +31,8 @@


def _select_entities_context_ids_sub_query(
start_day: dt,
end_day: dt,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
entity_ids: list[str],
json_quoted_entity_ids: list[str],
Expand All @@ -44,16 +43,18 @@ def _select_entities_context_ids_sub_query(
apply_event_entity_id_matchers(json_quoted_entity_ids)
),
apply_entities_hints(select(States.context_id))
.filter((States.last_updated > start_day) & (States.last_updated < end_day))
.filter(
(States.last_updated_ts > start_day) & (States.last_updated_ts < end_day)
)
.where(States.entity_id.in_(entity_ids)),
)
return select(union.c.context_id).group_by(union.c.context_id)


def _apply_entities_context_union(
query: Query,
start_day: dt,
end_day: dt,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
entity_ids: list[str],
json_quoted_entity_ids: list[str],
Expand Down Expand Up @@ -87,8 +88,8 @@ def _apply_entities_context_union(


def entities_stmt(
start_day: dt,
end_day: dt,
start_day: float,
end_day: float,
event_types: tuple[str, ...],
entity_ids: list[str],
json_quoted_entity_ids: list[str],
Expand All @@ -104,12 +105,12 @@ def entities_stmt(
event_types,
entity_ids,
json_quoted_entity_ids,
).order_by(Events.time_fired)
).order_by(Events.time_fired_ts)
)


def states_query_for_entity_ids(
start_day: dt, end_day: dt, entity_ids: list[str]
start_day: float, end_day: float, entity_ids: list[str]
) -> Query:
"""Generate a select for states from the States table for specific entities."""
return apply_states_filters(
Expand All @@ -136,5 +137,5 @@ def apply_event_entity_id_matchers(
def apply_entities_hints(query: Query) -> Query:
"""Force mysql to use the right index on large selects."""
return query.with_hint(
States, f"FORCE INDEX ({ENTITY_ID_LAST_UPDATED_INDEX})", dialect_name="mysql"
States, f"FORCE INDEX ({ENTITY_ID_LAST_UPDATED_INDEX_TS})", dialect_name="mysql"
)
Loading