From a4c1bcefb9d2a6f2aa0bc189fca496d46c78e3b0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 May 2022 18:12:15 -0500 Subject: [PATCH] Tune sqlite based on configured settings (#72016) --- homeassistant/components/recorder/util.py | 14 ++++- tests/components/recorder/test_util.py | 64 ++++++++++++++++++++--- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index eb99c304808..e464ac4126b 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -426,8 +426,18 @@ def setup_connection_for_dialect( version or version_string, "SQLite", MIN_VERSION_SQLITE ) - # approximately 8MiB of memory - execute_on_connection(dbapi_connection, "PRAGMA cache_size = -8192") + # The upper bound on the cache size is approximately 16MiB of memory + execute_on_connection(dbapi_connection, "PRAGMA cache_size = -16384") + + # + # Enable FULL synchronous if they have a commit interval of 0 + # or NORMAL if they do not. + # + # https://sqlite.org/pragma.html#pragma_synchronous + # The synchronous=NORMAL setting is a good choice for most applications running in WAL mode. + # + synchronous = "NORMAL" if instance.commit_interval else "FULL" + execute_on_connection(dbapi_connection, f"PRAGMA synchronous={synchronous}") # enable support for foreign keys execute_on_connection(dbapi_connection, "PRAGMA foreign_keys=ON") diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index c650ec20fb0..fea255647ec 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -234,18 +234,70 @@ def test_setup_connection_for_dialect_sqlite(sqlite_version, db_supports_row_num util.setup_connection_for_dialect(instance_mock, "sqlite", dbapi_connection, True) - assert len(execute_args) == 4 + assert len(execute_args) == 5 assert execute_args[0] == "PRAGMA journal_mode=WAL" assert execute_args[1] == "SELECT sqlite_version()" - assert execute_args[2] == "PRAGMA cache_size = -8192" - assert execute_args[3] == "PRAGMA foreign_keys=ON" + assert execute_args[2] == "PRAGMA cache_size = -16384" + assert execute_args[3] == "PRAGMA synchronous=NORMAL" + assert execute_args[4] == "PRAGMA foreign_keys=ON" execute_args = [] util.setup_connection_for_dialect(instance_mock, "sqlite", dbapi_connection, False) - assert len(execute_args) == 2 - assert execute_args[0] == "PRAGMA cache_size = -8192" - assert execute_args[1] == "PRAGMA foreign_keys=ON" + assert len(execute_args) == 3 + assert execute_args[0] == "PRAGMA cache_size = -16384" + assert execute_args[1] == "PRAGMA synchronous=NORMAL" + assert execute_args[2] == "PRAGMA foreign_keys=ON" + + assert instance_mock._db_supports_row_number == db_supports_row_number + + +@pytest.mark.parametrize( + "sqlite_version, db_supports_row_number", + [ + ("3.25.0", True), + ("3.24.0", False), + ], +) +def test_setup_connection_for_dialect_sqlite_zero_commit_interval( + sqlite_version, db_supports_row_number +): + """Test setting up the connection for a sqlite dialect with a zero commit interval.""" + instance_mock = MagicMock(_db_supports_row_number=True, commit_interval=0) + execute_args = [] + close_mock = MagicMock() + + def execute_mock(statement): + nonlocal execute_args + execute_args.append(statement) + + def fetchall_mock(): + nonlocal execute_args + if execute_args[-1] == "SELECT sqlite_version()": + return [[sqlite_version]] + return None + + def _make_cursor_mock(*_): + return MagicMock(execute=execute_mock, close=close_mock, fetchall=fetchall_mock) + + dbapi_connection = MagicMock(cursor=_make_cursor_mock) + + util.setup_connection_for_dialect(instance_mock, "sqlite", dbapi_connection, True) + + assert len(execute_args) == 5 + assert execute_args[0] == "PRAGMA journal_mode=WAL" + assert execute_args[1] == "SELECT sqlite_version()" + assert execute_args[2] == "PRAGMA cache_size = -16384" + assert execute_args[3] == "PRAGMA synchronous=FULL" + assert execute_args[4] == "PRAGMA foreign_keys=ON" + + execute_args = [] + util.setup_connection_for_dialect(instance_mock, "sqlite", dbapi_connection, False) + + assert len(execute_args) == 3 + assert execute_args[0] == "PRAGMA cache_size = -16384" + assert execute_args[1] == "PRAGMA synchronous=FULL" + assert execute_args[2] == "PRAGMA foreign_keys=ON" assert instance_mock._db_supports_row_number == db_supports_row_number