|
4 | 4 |
|
5 | 5 | import re |
6 | 6 | import shutil |
| 7 | +import sqlite3 |
7 | 8 | import sys |
8 | 9 | import time |
9 | 10 | import unicodedata |
10 | 11 | from collections.abc import Iterable, Iterator, Sequence |
| 12 | +from contextlib import closing |
11 | 13 | from dataclasses import dataclass |
12 | 14 | from datetime import UTC, datetime |
13 | 15 | from os import makedirs |
|
20 | 22 | URL, |
21 | 23 | ColumnExpressionArgument, |
22 | 24 | Engine, |
23 | | - NullPool, |
24 | 25 | ScalarResult, |
25 | 26 | Update, |
26 | 27 | and_, |
@@ -379,29 +380,21 @@ def open_library(self, library_dir: Path, in_memory: bool = False) -> OpenLibrar |
379 | 380 |
|
380 | 381 | @staticmethod |
381 | 382 | def __get_engine(library_dir: Path, in_memory: bool, sql_filename: str): |
| 383 | + db_path = library_dir / TS_FOLDER_NAME / sql_filename |
382 | 384 | connection_string = URL.create( |
383 | 385 | drivername="sqlite", |
384 | | - database=( |
385 | | - ":memory:" if in_memory else str(library_dir / TS_FOLDER_NAME / sql_filename) |
386 | | - ), |
| 386 | + database=":memory:" if in_memory else str(db_path), |
387 | 387 | ) |
388 | | - # NOTE: File-based databases should use NullPool to create new DB connection in order to |
389 | | - # keep connections on separate threads, which prevents the DB files from being locked |
390 | | - # even after a connection has been closed. |
391 | | - # SingletonThreadPool (the default for :memory:) should still be used for in-memory DBs. |
392 | | - # More info can be found on the SQLAlchemy docs: |
393 | | - # https://docs.sqlalchemy.org/en/20/changelog/migration_07.html |
394 | | - # Under -> sqlite-the-sqlite-dialect-now-uses-nullpool-for-file-based-databases |
395 | | - poolclass = None if in_memory else NullPool |
396 | 388 |
|
397 | | - logger.info( |
398 | | - "[Library] Creating SQLAlchemy Engine", |
399 | | - connection_string=connection_string, |
400 | | - poolclass=poolclass, |
401 | | - ) |
402 | | - return create_engine( |
403 | | - connection_string, poolclass=poolclass, connect_args={"autocommit": False} |
404 | | - ) |
| 389 | + if not in_memory: |
| 390 | + try: |
| 391 | + with closing(sqlite3.connect(db_path, autocommit=True)) as conn: |
| 392 | + conn.execute("PRAGMA journal_mode=WAL") |
| 393 | + except sqlite3.OperationalError as e: |
| 394 | + logger.warning("[Library] Could not set journal mode", error=e) |
| 395 | + |
| 396 | + logger.info("[Library] Creating SQLAlchemy Engine", connection_string=connection_string) |
| 397 | + return create_engine(connection_string, connect_args={"autocommit": False}) |
405 | 398 |
|
406 | 399 | def create_sqlite_library( |
407 | 400 | self, library_dir: Path, in_memory: bool, sql_filename: str = SQL_FILENAME |
@@ -1450,17 +1443,18 @@ def save_library_backup_to_disk(library_dir: Path) -> Path: |
1450 | 1443 | makedirs(str(library_dir / TS_FOLDER_NAME / BACKUP_FOLDER_NAME), exist_ok=True) |
1451 | 1444 |
|
1452 | 1445 | filename = f"ts_library_backup_{datetime.now(UTC).strftime('%Y_%m_%d_%H%M%S')}.sqlite" |
| 1446 | + library_backup_path = library_dir / TS_FOLDER_NAME / BACKUP_FOLDER_NAME / filename |
1453 | 1447 |
|
1454 | | - target_path = library_dir / TS_FOLDER_NAME / BACKUP_FOLDER_NAME / filename |
1455 | | - |
1456 | | - shutil.copyfile( |
1457 | | - library_dir / TS_FOLDER_NAME / SQL_FILENAME, |
1458 | | - target_path, |
1459 | | - ) |
| 1448 | + with ( |
| 1449 | + closing(sqlite3.connect(library_dir / TS_FOLDER_NAME / SQL_FILENAME)) as library, |
| 1450 | + closing(sqlite3.connect(library_backup_path)) as library_backup, |
| 1451 | + ): |
| 1452 | + library.backup(library_backup) |
| 1453 | + library_backup.execute("PRAGMA journal_mode=DELETE") # Don't use WAL for backups |
1460 | 1454 |
|
1461 | | - logger.info("Library backup saved to disk.", path=target_path) |
| 1455 | + logger.info("[Library] Library backup saved to disk", path=library_backup_path) |
1462 | 1456 |
|
1463 | | - return target_path |
| 1457 | + return library_backup_path |
1464 | 1458 |
|
1465 | 1459 | def get_tag(self, tag_id: int) -> Tag | None: |
1466 | 1460 | with Session(self.engine) as session: |
|
0 commit comments