mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Avoid writing registries to disk during startup (#112662)
This commit is contained in:
parent
0382d628a3
commit
caefdc6192
@ -14,6 +14,7 @@ from .normalized_name_base_registry import (
|
||||
NormalizedNameBaseRegistryItems,
|
||||
normalize_name,
|
||||
)
|
||||
from .registry import BaseRegistry
|
||||
from .storage import Store
|
||||
from .typing import UNDEFINED, UndefinedType
|
||||
|
||||
@ -22,7 +23,6 @@ EVENT_AREA_REGISTRY_UPDATED = "area_registry_updated"
|
||||
STORAGE_KEY = "core.area_registry"
|
||||
STORAGE_VERSION_MAJOR = 1
|
||||
STORAGE_VERSION_MINOR = 6
|
||||
SAVE_DELAY = 10
|
||||
|
||||
|
||||
class EventAreaRegistryUpdatedData(TypedDict):
|
||||
@ -86,7 +86,7 @@ class AreaRegistryStore(Store[dict[str, list[dict[str, Any]]]]):
|
||||
return old_data
|
||||
|
||||
|
||||
class AreaRegistry:
|
||||
class AreaRegistry(BaseRegistry):
|
||||
"""Class to hold a registry of areas."""
|
||||
|
||||
areas: NormalizedNameBaseRegistryItems[AreaEntry]
|
||||
@ -273,11 +273,6 @@ class AreaRegistry:
|
||||
self.areas = areas
|
||||
self._area_data = areas.data
|
||||
|
||||
@callback
|
||||
def async_schedule_save(self) -> None:
|
||||
"""Schedule saving the area registry."""
|
||||
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
|
||||
|
||||
@callback
|
||||
def _data_to_save(self) -> dict[str, list[dict[str, Any]]]:
|
||||
"""Return data of area registry to store in a file."""
|
||||
|
@ -31,6 +31,7 @@ from .deprecation import (
|
||||
)
|
||||
from .frame import report
|
||||
from .json import JSON_DUMP, find_paths_unserializable_data, json_bytes
|
||||
from .registry import BaseRegistry
|
||||
from .typing import UNDEFINED, UndefinedType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -45,7 +46,7 @@ EVENT_DEVICE_REGISTRY_UPDATED = "device_registry_updated"
|
||||
STORAGE_KEY = "core.device_registry"
|
||||
STORAGE_VERSION_MAJOR = 1
|
||||
STORAGE_VERSION_MINOR = 5
|
||||
SAVE_DELAY = 10
|
||||
|
||||
CLEANUP_DELAY = 10
|
||||
|
||||
CONNECTION_BLUETOOTH = "bluetooth"
|
||||
@ -456,7 +457,7 @@ class DeviceRegistryItems(UserDict[str, _EntryTypeT]):
|
||||
return None
|
||||
|
||||
|
||||
class DeviceRegistry:
|
||||
class DeviceRegistry(BaseRegistry):
|
||||
"""Class to hold a registry of devices."""
|
||||
|
||||
devices: DeviceRegistryItems[DeviceEntry]
|
||||
@ -898,11 +899,6 @@ class DeviceRegistry:
|
||||
self.deleted_devices = deleted_devices
|
||||
self._device_data = devices.data
|
||||
|
||||
@callback
|
||||
def async_schedule_save(self) -> None:
|
||||
"""Schedule saving the device registry."""
|
||||
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
|
||||
|
||||
@callback
|
||||
def _data_to_save(self) -> dict[str, list[dict[str, Any]]]:
|
||||
"""Return data of device registry to store in a file."""
|
||||
|
@ -52,6 +52,7 @@ from homeassistant.util.read_only_dict import ReadOnlyDict
|
||||
from . import device_registry as dr, storage
|
||||
from .device_registry import EVENT_DEVICE_REGISTRY_UPDATED
|
||||
from .json import JSON_DUMP, find_paths_unserializable_data, json_bytes
|
||||
from .registry import BaseRegistry
|
||||
from .typing import UNDEFINED, UndefinedType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -61,7 +62,7 @@ T = TypeVar("T")
|
||||
|
||||
DATA_REGISTRY = "entity_registry"
|
||||
EVENT_ENTITY_REGISTRY_UPDATED = "entity_registry_updated"
|
||||
SAVE_DELAY = 10
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
STORAGE_VERSION_MAJOR = 1
|
||||
@ -549,7 +550,7 @@ class EntityRegistryItems(UserDict[str, RegistryEntry]):
|
||||
return [data[key] for key in self._area_id_index.get(area_id, ())]
|
||||
|
||||
|
||||
class EntityRegistry:
|
||||
class EntityRegistry(BaseRegistry):
|
||||
"""Class to hold a registry of entities."""
|
||||
|
||||
deleted_entities: dict[tuple[str, str, str], DeletedRegistryEntry]
|
||||
@ -1182,11 +1183,6 @@ class EntityRegistry:
|
||||
self.entities = entities
|
||||
self._entities_data = entities.data
|
||||
|
||||
@callback
|
||||
def async_schedule_save(self) -> None:
|
||||
"""Schedule saving the entity registry."""
|
||||
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
|
||||
|
||||
@callback
|
||||
def _data_to_save(self) -> dict[str, Any]:
|
||||
"""Return data of entity registry to store in a file."""
|
||||
|
@ -14,6 +14,7 @@ from .normalized_name_base_registry import (
|
||||
NormalizedNameBaseRegistryItems,
|
||||
normalize_name,
|
||||
)
|
||||
from .registry import BaseRegistry
|
||||
from .storage import Store
|
||||
from .typing import UNDEFINED, EventType, UndefinedType
|
||||
|
||||
@ -21,7 +22,6 @@ DATA_REGISTRY = "floor_registry"
|
||||
EVENT_FLOOR_REGISTRY_UPDATED = "floor_registry_updated"
|
||||
STORAGE_KEY = "core.floor_registry"
|
||||
STORAGE_VERSION_MAJOR = 1
|
||||
SAVE_DELAY = 10
|
||||
|
||||
|
||||
class EventFloorRegistryUpdatedData(TypedDict):
|
||||
@ -44,7 +44,7 @@ class FloorEntry(NormalizedNameBaseRegistryEntry):
|
||||
level: int = 0
|
||||
|
||||
|
||||
class FloorRegistry:
|
||||
class FloorRegistry(BaseRegistry):
|
||||
"""Class to hold a registry of floors."""
|
||||
|
||||
floors: NormalizedNameBaseRegistryItems[FloorEntry]
|
||||
@ -209,11 +209,6 @@ class FloorRegistry:
|
||||
self.floors = floors
|
||||
self._floor_data = floors.data
|
||||
|
||||
@callback
|
||||
def async_schedule_save(self) -> None:
|
||||
"""Schedule saving the floor registry."""
|
||||
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
|
||||
|
||||
@callback
|
||||
def _data_to_save(self) -> dict[str, list[dict[str, str | int | list[str] | None]]]:
|
||||
"""Return data of floor registry to store in a file."""
|
||||
|
@ -14,6 +14,7 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.util.async_ import run_callback_threadsafe
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .registry import BaseRegistry
|
||||
from .storage import Store
|
||||
|
||||
DATA_REGISTRY = "issue_registry"
|
||||
@ -21,7 +22,6 @@ EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED = "repairs_issue_registry_updated"
|
||||
STORAGE_KEY = "repairs.issue_registry"
|
||||
STORAGE_VERSION_MAJOR = 1
|
||||
STORAGE_VERSION_MINOR = 2
|
||||
SAVE_DELAY = 10
|
||||
|
||||
|
||||
class IssueSeverity(StrEnum):
|
||||
@ -92,7 +92,7 @@ class IssueRegistryStore(Store[dict[str, list[dict[str, Any]]]]):
|
||||
return old_data
|
||||
|
||||
|
||||
class IssueRegistry:
|
||||
class IssueRegistry(BaseRegistry):
|
||||
"""Class to hold a registry of issues."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, *, read_only: bool = False) -> None:
|
||||
@ -259,11 +259,6 @@ class IssueRegistry:
|
||||
|
||||
self.issues = issues
|
||||
|
||||
@callback
|
||||
def async_schedule_save(self) -> None:
|
||||
"""Schedule saving the issue registry."""
|
||||
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
|
||||
|
||||
@callback
|
||||
def _data_to_save(self) -> dict[str, list[dict[str, str | None]]]:
|
||||
"""Return data of issue registry to store in a file."""
|
||||
|
@ -14,6 +14,7 @@ from .normalized_name_base_registry import (
|
||||
NormalizedNameBaseRegistryItems,
|
||||
normalize_name,
|
||||
)
|
||||
from .registry import BaseRegistry
|
||||
from .storage import Store
|
||||
from .typing import UNDEFINED, EventType, UndefinedType
|
||||
|
||||
@ -21,7 +22,6 @@ DATA_REGISTRY = "label_registry"
|
||||
EVENT_LABEL_REGISTRY_UPDATED = "label_registry_updated"
|
||||
STORAGE_KEY = "core.label_registry"
|
||||
STORAGE_VERSION_MAJOR = 1
|
||||
SAVE_DELAY = 10
|
||||
|
||||
|
||||
class EventLabelRegistryUpdatedData(TypedDict):
|
||||
@ -44,7 +44,7 @@ class LabelEntry(NormalizedNameBaseRegistryEntry):
|
||||
icon: str | None = None
|
||||
|
||||
|
||||
class LabelRegistry:
|
||||
class LabelRegistry(BaseRegistry):
|
||||
"""Class to hold a registry of labels."""
|
||||
|
||||
labels: NormalizedNameBaseRegistryItems[LabelEntry]
|
||||
@ -205,11 +205,6 @@ class LabelRegistry:
|
||||
self.labels = labels
|
||||
self._label_data = labels.data
|
||||
|
||||
@callback
|
||||
def async_schedule_save(self) -> None:
|
||||
"""Schedule saving the label registry."""
|
||||
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
|
||||
|
||||
@callback
|
||||
def _data_to_save(self) -> dict[str, list[dict[str, str | None]]]:
|
||||
"""Return data of label registry to store in a file."""
|
||||
|
35
homeassistant/helpers/registry.py
Normal file
35
homeassistant/helpers/registry.py
Normal file
@ -0,0 +1,35 @@
|
||||
"""Provide a base implementation for registries."""
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from homeassistant.core import CoreState, HomeAssistant, callback
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .storage import Store
|
||||
|
||||
SAVE_DELAY = 10
|
||||
SAVE_DELAY_STARTING = 300
|
||||
|
||||
|
||||
class BaseRegistry(ABC):
|
||||
"""Class to implement a registry."""
|
||||
|
||||
hass: HomeAssistant
|
||||
_store: Store
|
||||
|
||||
@callback
|
||||
def async_schedule_save(self) -> None:
|
||||
"""Schedule saving the registry."""
|
||||
# Schedule the save past startup to avoid writing
|
||||
# the file while the system is starting.
|
||||
delay = (
|
||||
SAVE_DELAY_STARTING if self.hass.state is CoreState.starting else SAVE_DELAY
|
||||
)
|
||||
self._store.async_delay_save(self._data_to_save, delay)
|
||||
|
||||
@callback
|
||||
@abstractmethod
|
||||
def _data_to_save(self) -> dict[str, Any]:
|
||||
"""Return data of registry to store in a file."""
|
51
tests/helpers/test_registry.py
Normal file
51
tests/helpers/test_registry.py
Normal file
@ -0,0 +1,51 @@
|
||||
"""Tests for the registry."""
|
||||
from typing import Any
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
|
||||
from homeassistant.core import CoreState, HomeAssistant
|
||||
from homeassistant.helpers import storage
|
||||
from homeassistant.helpers.registry import SAVE_DELAY, SAVE_DELAY_STARTING, BaseRegistry
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
|
||||
class SampleRegistry(BaseRegistry):
|
||||
"""Class to hold a registry of X."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
"""Initialize the registry."""
|
||||
self.hass = hass
|
||||
self._store = storage.Store(hass, 1, "test")
|
||||
self.save_calls = 0
|
||||
|
||||
def _data_to_save(self) -> None:
|
||||
"""Return data of registry to save."""
|
||||
self.save_calls += 1
|
||||
return None
|
||||
|
||||
|
||||
async def test_async_schedule_save(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, hass_storage: dict[str, Any]
|
||||
) -> None:
|
||||
"""Test saving the registry."""
|
||||
registry = SampleRegistry(hass)
|
||||
hass.set_state(CoreState.starting)
|
||||
|
||||
registry.async_schedule_save()
|
||||
freezer.tick(SAVE_DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert registry.save_calls == 0
|
||||
|
||||
freezer.tick(SAVE_DELAY_STARTING)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert registry.save_calls == 1
|
||||
|
||||
hass.set_state(CoreState.running)
|
||||
registry.async_schedule_save()
|
||||
freezer.tick(SAVE_DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert registry.save_calls == 2
|
Loading…
x
Reference in New Issue
Block a user