Refactor HistoryManager to use persistent DB connection

Replaces per-operation SQLite connections with a persistent connection for improved efficiency and thread safety. Adds additional indexes for faster search on title, channel, and URL. Refactors all database access methods to use the persistent connection and simplifies transaction handling.
This commit is contained in:
oop7
2026-01-21 18:05:17 +02:00
parent 5f17fabc50
commit 7c2e461141
+36 -18
View File
@@ -50,6 +50,7 @@ class HistoryManager:
_lock = threading.RLock()
# Define DB file next to the old JSON file
_db_file = APP_DATA_DIR / "ytsage_history.db"
_connection = None
_initialized = False
@classmethod
@@ -67,8 +68,12 @@ class HistoryManager:
# Ensure directory exists
cls._db_file.parent.mkdir(parents=True, exist_ok=True)
with sqlite3.connect(cls._db_file, check_same_thread=False) as conn:
cursor = conn.cursor()
# We use a persistent connection to avoid churn
if cls._connection is None:
cls._connection = sqlite3.connect(cls._db_file, check_same_thread=False)
cls._connection.row_factory = sqlite3.Row
cursor = cls._connection.cursor()
# Create table
cursor.execute("""
@@ -96,7 +101,13 @@ class HistoryManager:
ON history (timestamp DESC)
""")
conn.commit()
# Indexes for faster search (title, channel, url)
# This prevents full table scans during search
cursor.execute("CREATE INDEX IF NOT EXISTS idx_title ON history (title)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_channel ON history (channel)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_url ON history (url)")
cls._connection.commit()
# If we just created the DB and have a JSON file, migrate
if not db_exists and legacy_json_exists:
@@ -117,7 +128,10 @@ class HistoryManager:
if isinstance(data, list):
count = 0
with sqlite3.connect(cls._db_file, check_same_thread=False) as conn:
# Use the persistent connection
conn = cls._get_connection()
try:
with conn: # Transaction
cursor = conn.cursor()
for entry in data:
try:
@@ -159,8 +173,6 @@ class HistoryManager:
except Exception as e:
logger.error(f"Skipped invalid entry during migration: {e}")
conn.commit()
logger.info(f"Successfully migrated {count} history entries.")
# Rename old JSON to .bak to avoid re-migration, or keep as backup
@@ -169,14 +181,21 @@ class HistoryManager:
except Exception as e:
logger.warning(f"Could not rename legacy history file: {e}")
except Exception as e:
logger.error(f"Migration transaction failed: {e}")
except Exception as e:
logger.error(f"Migration failed: {e}")
@classmethod
def _get_connection(cls):
"""Get a database connection."""
"""Get the persistent database connection."""
cls._init_db()
return sqlite3.connect(cls._db_file, check_same_thread=False)
if cls._connection is None:
# Should be created in _init_db, but just in case
cls._connection = sqlite3.connect(cls._db_file, check_same_thread=False)
cls._connection.row_factory = sqlite3.Row
return cls._connection
@classmethod
def get_all_entries(cls, limit: Optional[int] = None) -> List[Dict[str, Any]]:
@@ -192,9 +211,10 @@ class HistoryManager:
entries = []
try:
with cls._lock: # Lock for simple concurrency safety
with cls._get_connection() as conn:
# Return dict-like rows
conn.row_factory = sqlite3.Row
# Use persistent connection
conn = cls._get_connection()
# conn.row_factory is already set in _init_db/_get_connection
cursor = conn.cursor()
query = "SELECT * FROM history ORDER BY timestamp DESC"
@@ -237,8 +257,7 @@ class HistoryManager:
"""
try:
with cls._lock:
with cls._get_connection() as conn:
conn.row_factory = sqlite3.Row
conn = cls._get_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM history WHERE id = ?", (entry_id,))
row = cursor.fetchone()
@@ -299,7 +318,7 @@ class HistoryManager:
try:
with cls._lock:
with cls._get_connection() as conn:
conn = cls._get_connection()
cursor = conn.cursor()
cursor.execute("""
@@ -338,7 +357,7 @@ class HistoryManager:
"""Remove an entry from history by ID."""
try:
with cls._lock:
with cls._get_connection() as conn:
conn = cls._get_connection()
cursor = conn.cursor()
cursor.execute("DELETE FROM history WHERE id = ?", (entry_id,))
if cursor.rowcount > 0:
@@ -355,7 +374,7 @@ class HistoryManager:
"""Clear all history entries."""
try:
with cls._lock:
with cls._get_connection() as conn:
conn = cls._get_connection()
cursor = conn.cursor()
cursor.execute("DELETE FROM history")
count = cursor.rowcount
@@ -384,8 +403,7 @@ class HistoryManager:
try:
search_pattern = f"%{query}%"
with cls._lock:
with cls._get_connection() as conn:
conn.row_factory = sqlite3.Row
conn = cls._get_connection()
cursor = conn.cursor()
cursor.execute("""
SELECT * FROM history