Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0a9927d

Browse files
authored
Avoid locking the database for non-SQLite backends (home-assistant#63847)
* Avoid locking the database for non-SQLite backends Currently we only have a lock implementation for SQLite. Just return success for all other databases as they are not expected to store data in the config directory and the caller can assume that a backup can be safely taken. This fixes `RuntimeError: generator didn't yield` errors when creating a backup with the current Supervisor dev builds.
1 parent f2a6118 commit 0a9927d

File tree

4 files changed

+52
-18
lines changed

4 files changed

+52
-18
lines changed

‎homeassistant/components/recorder/__init__.py‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
session_scope,
7979
setup_connection_for_dialect,
8080
validate_or_move_away_sqlite_database,
81-
write_lock_db,
81+
write_lock_db_sqlite,
8282
)
8383

8484
_LOGGER = logging.getLogger(__name__)
@@ -870,7 +870,7 @@ def _lock_database(self, task: DatabaseLockTask):
870870
def _async_set_database_locked(task: DatabaseLockTask):
871871
task.database_locked.set()
872872

873-
with write_lock_db(self):
873+
with write_lock_db_sqlite(self):
874874
# Notify that lock is being held, wait until database can be used again.
875875
self.hass.add_job(_async_set_database_locked, task)
876876
while not task.database_unlock.wait(timeout=DB_LOCK_QUEUE_CHECK_TIMEOUT):
@@ -1057,6 +1057,12 @@ def block_till_done(self):
10571057

10581058
async def lock_database(self) -> bool:
10591059
"""Lock database so it can be backed up safely."""
1060+
if not self.engine or self.engine.dialect.name != "sqlite":
1061+
_LOGGER.debug(
1062+
"Not a SQLite database or not connected, locking not necessary"
1063+
)
1064+
return True
1065+
10601066
if self._database_lock_task:
10611067
_LOGGER.warning("Database already locked")
10621068
return False
@@ -1080,6 +1086,12 @@ def unlock_database(self) -> bool:
10801086
10811087
Returns true if database lock has been held throughout the process.
10821088
"""
1089+
if not self.engine or self.engine.dialect.name != "sqlite":
1090+
_LOGGER.debug(
1091+
"Not a SQLite database or not connected, unlocking not necessary"
1092+
)
1093+
return True
1094+
10831095
if not self._database_lock_task:
10841096
_LOGGER.warning("Database currently not locked")
10851097
return False

‎homeassistant/components/recorder/util.py‎

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -462,22 +462,21 @@ def perodic_db_cleanups(instance: Recorder):
462462

463463

464464
@contextmanager
465-
def write_lock_db(instance: Recorder):
465+
def write_lock_db_sqlite(instance: Recorder):
466466
"""Lock database for writes."""
467467

468-
if instance.engine.dialect.name == "sqlite":
469-
with instance.engine.connect() as connection:
470-
# Execute sqlite to create a wal checkpoint
471-
# This is optional but makes sure the backup is going to be minimal
472-
connection.execute(text("PRAGMA wal_checkpoint(TRUNCATE)"))
473-
# Create write lock
474-
_LOGGER.debug("Lock database")
475-
connection.execute(text("BEGIN IMMEDIATE;"))
476-
try:
477-
yield
478-
finally:
479-
_LOGGER.debug("Unlock database")
480-
connection.execute(text("END;"))
468+
with instance.engine.connect() as connection:
469+
# Execute sqlite to create a wal checkpoint
470+
# This is optional but makes sure the backup is going to be minimal
471+
connection.execute(text("PRAGMA wal_checkpoint(TRUNCATE)"))
472+
# Create write lock
473+
_LOGGER.debug("Lock database")
474+
connection.execute(text("BEGIN IMMEDIATE;"))
475+
try:
476+
yield
477+
finally:
478+
_LOGGER.debug("Unlock database")
479+
connection.execute(text("END;"))
481480

482481

483482
def async_migration_in_progress(hass: HomeAssistant) -> bool:

‎tests/components/recorder/test_init.py‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import asyncio
44
from datetime import datetime, timedelta
55
import sqlite3
6+
import threading
67
from unittest.mock import patch
78

89
import pytest
@@ -1204,12 +1205,34 @@ async def test_database_lock_timeout(hass):
12041205
"""Test locking database timeout when recorder stopped."""
12051206
await async_init_recorder_component(hass)
12061207
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
1207-
await hass.async_block_till_done()
12081208

12091209
instance: Recorder = hass.data[DATA_INSTANCE]
1210+
1211+
class BlockQueue(recorder.RecorderTask):
1212+
event: threading.Event = threading.Event()
1213+
1214+
def run(self, instance: Recorder) -> None:
1215+
self.event.wait()
1216+
1217+
block_task = BlockQueue()
1218+
instance.queue.put(block_task)
12101219
with patch.object(recorder, "DB_LOCK_TIMEOUT", 0.1):
12111220
try:
12121221
with pytest.raises(TimeoutError):
12131222
await instance.lock_database()
12141223
finally:
12151224
instance.unlock_database()
1225+
block_task.event.set()
1226+
1227+
1228+
async def test_database_lock_without_instance(hass):
1229+
"""Test database lock doesn't fail if instance is not initialized."""
1230+
await async_init_recorder_component(hass)
1231+
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
1232+
1233+
instance: Recorder = hass.data[DATA_INSTANCE]
1234+
with patch.object(instance, "engine", None):
1235+
try:
1236+
assert await instance.lock_database()
1237+
finally:
1238+
assert instance.unlock_database()

‎tests/components/recorder/test_util.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ async def test_write_lock_db(hass, tmp_path):
570570

571571
instance = hass.data[DATA_INSTANCE]
572572

573-
with util.write_lock_db(instance):
573+
with util.write_lock_db_sqlite(instance):
574574
# Database should be locked now, try writing SQL command
575575
with instance.engine.connect() as connection:
576576
with pytest.raises(OperationalError):

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /