Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dialect (database engine) and version to recorder system health data #72339

Merged
merged 3 commits into from May 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions homeassistant/components/recorder/core.py
Expand Up @@ -12,6 +12,7 @@
import time
from typing import Any, TypeVar, cast

from awesomeversion import AwesomeVersion
from lru import LRU # pylint: disable=no-name-in-module
from sqlalchemy import create_engine, event as sqlalchemy_event, exc, func, select
from sqlalchemy.engine import Engine
Expand Down Expand Up @@ -159,6 +160,7 @@ def __init__(
self.db_url = uri
self.db_max_retries = db_max_retries
self.db_retry_wait = db_retry_wait
self.engine_version: AwesomeVersion | None = None
self.async_db_ready: asyncio.Future[bool] = asyncio.Future()
self.async_recorder_ready = asyncio.Event()
self._queue_watch = threading.Event()
Expand Down Expand Up @@ -1009,12 +1011,13 @@ def setup_recorder_connection(
) -> None:
"""Dbapi specific connection settings."""
assert self.engine is not None
setup_connection_for_dialect(
if version := setup_connection_for_dialect(
self,
self.engine.dialect.name,
dbapi_connection,
not self._completed_first_database_setup,
)
):
self.engine_version = version
self._completed_first_database_setup = True

if self.db_url == SQLITE_URL_PREFIX or ":memory:" in self.db_url:
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/components/recorder/strings.json
Expand Up @@ -3,7 +3,9 @@
"info": {
"oldest_recorder_run": "Oldest Run Start Time",
"current_recorder_run": "Current Run Start Time",
"estimated_db_size": "Estimated Database Size (MiB)"
"estimated_db_size": "Estimated Database Size (MiB)",
"database_engine": "Database Engine",
"database_version": "Database Version"
}
}
}
23 changes: 19 additions & 4 deletions homeassistant/components/recorder/system_health/__init__.py
Expand Up @@ -44,17 +44,32 @@ def _get_db_stats(instance: Recorder, database_name: str) -> dict[str, Any]:
return db_stats


@callback
def _async_get_db_engine_info(instance: Recorder) -> dict[str, Any]:
"""Get database engine info."""
db_engine_info: dict[str, Any] = {}
if dialect_name := instance.dialect_name:
db_engine_info["database_engine"] = dialect_name.value
if engine_version := instance.engine_version:
db_engine_info["database_version"] = str(engine_version)
return db_engine_info


async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
"""Get info for the info page."""
instance = get_instance(hass)

run_history = instance.run_history
database_name = URL(instance.db_url).path.lstrip("/")
db_engine_info = _async_get_db_engine_info(instance)
db_stats: dict[str, Any] = {}

if instance.async_db_ready.done():
db_stats = await instance.async_add_executor_job(
_get_db_stats, instance, database_name
)
return {
"oldest_recorder_run": run_history.first.start,
"current_recorder_run": run_history.current.start,
} | db_stats
db_runs = {
"oldest_recorder_run": run_history.first.start,
"current_recorder_run": run_history.current.start,
}
return db_runs | db_stats | db_engine_info
2 changes: 2 additions & 0 deletions homeassistant/components/recorder/translations/en.json
Expand Up @@ -2,6 +2,8 @@
"system_health": {
"info": {
"current_recorder_run": "Current Run Start Time",
"database_engine": "Database Engine",
"database_version": "Database Version",
"estimated_db_size": "Estimated Database Size (MiB)",
"oldest_recorder_run": "Oldest Run Start Time"
}
Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/recorder/util.py
Expand Up @@ -396,8 +396,9 @@ def setup_connection_for_dialect(
dialect_name: str,
dbapi_connection: Any,
first_connection: bool,
) -> None:
) -> AwesomeVersion | None:
"""Execute statements needed for dialect connection."""
version: AwesomeVersion | None = None
if dialect_name == SupportedDialect.SQLITE:
if first_connection:
old_isolation = dbapi_connection.isolation_level
Expand Down Expand Up @@ -465,6 +466,8 @@ def setup_connection_for_dialect(
else:
_fail_unsupported_dialect(dialect_name)

return version


def end_incomplete_runs(session: Session, start_time: datetime) -> None:
"""End any incomplete recorder runs."""
Expand Down
6 changes: 6 additions & 0 deletions tests/components/recorder/test_system_health.py
Expand Up @@ -24,6 +24,8 @@ async def test_recorder_system_health(hass, recorder_mock):
"current_recorder_run": instance.run_history.current.start,
"oldest_recorder_run": instance.run_history.first.start,
"estimated_db_size": ANY,
"database_engine": SupportedDialect.SQLITE.value,
"database_version": ANY,
}


Expand All @@ -46,6 +48,8 @@ async def test_recorder_system_health_alternate_dbms(hass, recorder_mock, dialec
"current_recorder_run": instance.run_history.current.start,
"oldest_recorder_run": instance.run_history.first.start,
"estimated_db_size": "1.00 MiB",
"database_engine": dialect_name.value,
"database_version": ANY,
}


Expand All @@ -62,4 +66,6 @@ async def test_recorder_system_health_crashed_recorder_runs_table(
"current_recorder_run": instance.run_history.current.start,
"oldest_recorder_run": instance.run_history.current.start,
"estimated_db_size": ANY,
"database_engine": SupportedDialect.SQLITE.value,
"database_version": ANY,
}