mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 09:17:10 +00:00
Ensure all recorder session executes use retries or the execute helper (#89888)
This commit is contained in:
parent
fd5b57ae6c
commit
377dff5ee4
@ -43,6 +43,12 @@ KEEPALIVE_TIME = 30
|
||||
EXCLUDE_ATTRIBUTES = f"{DOMAIN}_exclude_attributes_by_domain"
|
||||
|
||||
|
||||
STATISTICS_ROWS_SCHEMA_VERSION = 23
|
||||
CONTEXT_ID_AS_BINARY_SCHEMA_VERSION = 36
|
||||
EVENT_TYPE_IDS_SCHEMA_VERSION = 37
|
||||
STATES_META_SCHEMA_VERSION = 38
|
||||
|
||||
|
||||
class SupportedDialect(StrEnum):
|
||||
"""Supported dialects."""
|
||||
|
||||
|
@ -41,8 +41,10 @@ from homeassistant.util.enum import try_parse_enum
|
||||
|
||||
from . import migration, statistics
|
||||
from .const import (
|
||||
CONTEXT_ID_AS_BINARY_SCHEMA_VERSION,
|
||||
DB_WORKER_PREFIX,
|
||||
DOMAIN,
|
||||
EVENT_TYPE_IDS_SCHEMA_VERSION,
|
||||
KEEPALIVE_TIME,
|
||||
MARIADB_PYMYSQL_URL_PREFIX,
|
||||
MARIADB_URL_PREFIX,
|
||||
@ -50,6 +52,8 @@ from .const import (
|
||||
MYSQLDB_PYMYSQL_URL_PREFIX,
|
||||
MYSQLDB_URL_PREFIX,
|
||||
SQLITE_URL_PREFIX,
|
||||
STATES_META_SCHEMA_VERSION,
|
||||
STATISTICS_ROWS_SCHEMA_VERSION,
|
||||
SupportedDialect,
|
||||
)
|
||||
from .db_schema import (
|
||||
@ -108,6 +112,7 @@ from .util import (
|
||||
build_mysqldb_conv,
|
||||
dburl_to_path,
|
||||
end_incomplete_runs,
|
||||
execute_stmt_lambda_element,
|
||||
is_second_sunday,
|
||||
move_away_broken_database,
|
||||
session_scope,
|
||||
@ -688,24 +693,28 @@ class Recorder(threading.Thread):
|
||||
# since we want the frontend queries to avoid a thundering
|
||||
# herd of queries to find the statistics meta data if
|
||||
# there are a lot of statistics graphs on the frontend.
|
||||
if self.schema_version >= 23:
|
||||
if self.schema_version >= STATISTICS_ROWS_SCHEMA_VERSION:
|
||||
self.statistics_meta_manager.load(session)
|
||||
|
||||
if (
|
||||
self.schema_version < 36
|
||||
or session.execute(has_events_context_ids_to_migrate()).scalar()
|
||||
self.schema_version < CONTEXT_ID_AS_BINARY_SCHEMA_VERSION
|
||||
or execute_stmt_lambda_element(
|
||||
session, has_events_context_ids_to_migrate()
|
||||
)
|
||||
):
|
||||
self.queue_task(StatesContextIDMigrationTask())
|
||||
|
||||
if (
|
||||
self.schema_version < 36
|
||||
or session.execute(has_states_context_ids_to_migrate()).scalar()
|
||||
self.schema_version < CONTEXT_ID_AS_BINARY_SCHEMA_VERSION
|
||||
or execute_stmt_lambda_element(
|
||||
session, has_states_context_ids_to_migrate()
|
||||
)
|
||||
):
|
||||
self.queue_task(EventsContextIDMigrationTask())
|
||||
|
||||
if (
|
||||
self.schema_version < 37
|
||||
or session.execute(has_event_type_to_migrate()).scalar()
|
||||
self.schema_version < EVENT_TYPE_IDS_SCHEMA_VERSION
|
||||
or execute_stmt_lambda_element(session, has_event_type_to_migrate())
|
||||
):
|
||||
self.queue_task(EventTypeIDMigrationTask())
|
||||
else:
|
||||
@ -713,8 +722,8 @@ class Recorder(threading.Thread):
|
||||
self.event_type_manager.active = True
|
||||
|
||||
if (
|
||||
self.schema_version < 38
|
||||
or session.execute(has_entity_ids_to_migrate()).scalar()
|
||||
self.schema_version < STATES_META_SCHEMA_VERSION
|
||||
or execute_stmt_lambda_element(session, has_entity_ids_to_migrate())
|
||||
):
|
||||
self.queue_task(EntityIDMigrationTask())
|
||||
else:
|
||||
@ -1002,7 +1011,7 @@ class Recorder(threading.Thread):
|
||||
if states_meta_manager.active:
|
||||
dbstate.entity_id = None
|
||||
|
||||
self.event_session.add(dbstate)
|
||||
session.add(dbstate)
|
||||
|
||||
def _handle_database_error(self, err: Exception) -> bool:
|
||||
"""Handle a database error that may result in moving away the corrupt db."""
|
||||
@ -1015,9 +1024,9 @@ class Recorder(threading.Thread):
|
||||
return False
|
||||
|
||||
def _event_session_has_pending_writes(self) -> bool:
|
||||
return bool(
|
||||
self.event_session and (self.event_session.new or self.event_session.dirty)
|
||||
)
|
||||
"""Return True if there are pending writes in the event session."""
|
||||
session = self.event_session
|
||||
return bool(session and (session.new or session.dirty))
|
||||
|
||||
def _commit_event_session_or_retry(self) -> None:
|
||||
"""Commit the event session if there is work to do."""
|
||||
@ -1043,9 +1052,10 @@ class Recorder(threading.Thread):
|
||||
|
||||
def _commit_event_session(self) -> None:
|
||||
assert self.event_session is not None
|
||||
session = self.event_session
|
||||
self._commits_without_expire += 1
|
||||
|
||||
self.event_session.commit()
|
||||
session.commit()
|
||||
# We just committed the state attributes to the database
|
||||
# and we now know the attributes_ids. We can save
|
||||
# many selects for matching attributes by loading them
|
||||
@ -1061,7 +1071,7 @@ class Recorder(threading.Thread):
|
||||
# do it after EXPIRE_AFTER_COMMITS commits
|
||||
if self._commits_without_expire >= EXPIRE_AFTER_COMMITS:
|
||||
self._commits_without_expire = 0
|
||||
self.event_session.expire_all()
|
||||
session.expire_all()
|
||||
|
||||
def _handle_sqlite_corruption(self) -> None:
|
||||
"""Handle the sqlite3 database being corrupt."""
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Support managing StatesMeta."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import Iterable, Sequence
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from sqlalchemy.orm.session import Session
|
||||
@ -63,7 +63,14 @@ class StatesMetaManager(BaseLRUTableManager[StatesMeta]):
|
||||
This call is always thread-safe.
|
||||
"""
|
||||
with session.no_autoflush:
|
||||
return dict(tuple(session.execute(find_all_states_metadata_ids()))) # type: ignore[arg-type]
|
||||
return dict(
|
||||
cast(
|
||||
Sequence[tuple[int, str]],
|
||||
execute_stmt_lambda_element(
|
||||
session, find_all_states_metadata_ids()
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
def get_many(
|
||||
self, entity_ids: Iterable[str], session: Session, from_recorder: bool
|
||||
|
Loading…
x
Reference in New Issue
Block a user