don't crash server when sqlite-zstd extension can't be loaded
Build and Push Docker Container / build-and-push (push) Successful in 3m58s
Build and Push Docker Container / build-and-push (push) Successful in 3m58s
This commit is contained in:
@@ -1,13 +1,21 @@
|
||||
from datetime import datetime, timezone
|
||||
import asyncio, sqlite3, json, os
|
||||
import asyncio, sqlite3, json, os, logging, sys
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
if not logger.handlers:
|
||||
_handler = logging.StreamHandler(stream=sys.stdout)
|
||||
_handler.setFormatter(logging.Formatter(fmt="%(levelname)s %(module)s: %(message)s"))
|
||||
logger.addHandler(_handler)
|
||||
logger.propagate = False
|
||||
|
||||
_ZSTD_EXT = Path(os.environ.get("SQLITE_ZSTD_EXT", "/usr/local/lib/libsqlite_zstd.so")).expanduser().resolve()
|
||||
|
||||
class GameplayDatabase:
|
||||
def __init__(self, db_path:str, busy_timeout_ms:int=5000):
|
||||
self.db_path = db_path
|
||||
self.busy_timeout_ms = max(1000, int(busy_timeout_ms))
|
||||
self._zstd_available = False
|
||||
self._initialize_database()
|
||||
|
||||
def _connect(self) -> sqlite3.Connection:
|
||||
@@ -16,13 +24,18 @@ class GameplayDatabase:
|
||||
timeout=max(1, self.busy_timeout_ms // 1000),
|
||||
isolation_level=None,
|
||||
)
|
||||
|
||||
if Path(_ZSTD_EXT).exists():
|
||||
connection.enable_load_extension(True)
|
||||
connection.load_extension(str(_ZSTD_EXT))
|
||||
connection.enable_load_extension(False)
|
||||
|
||||
connection.row_factory = sqlite3.Row
|
||||
|
||||
if _ZSTD_EXT.exists():
|
||||
try:
|
||||
connection.enable_load_extension(True)
|
||||
connection.load_extension(str(_ZSTD_EXT))
|
||||
self._zstd_available = True
|
||||
except sqlite3.OperationalError as e:
|
||||
logger.warning(f"sqlite-zstd extension skipped: {e}")
|
||||
finally:
|
||||
connection.enable_load_extension(False)
|
||||
|
||||
connection.execute("PRAGMA foreign_keys = ON")
|
||||
connection.execute("PRAGMA journal_mode = WAL")
|
||||
connection.execute("PRAGMA synchronous = NORMAL")
|
||||
@@ -102,7 +115,8 @@ class GameplayDatabase:
|
||||
self._ensure_column_exists(connection, "games", "your_snake_version", "TEXT")
|
||||
self._ensure_column_exists(connection, "games", "game_type", "TEXT")
|
||||
self._ensure_column_exists(connection, "snake_turns", "latency", "TEXT")
|
||||
self._enable_zstd_compression(connection)
|
||||
if self._zstd_available:
|
||||
self._enable_zstd_compression(connection)
|
||||
connection.execute("PRAGMA optimize")
|
||||
|
||||
def _create_indexes_if_tables(self, connection: sqlite3.Connection) -> None:
|
||||
@@ -121,11 +135,27 @@ class GameplayDatabase:
|
||||
connection.execute(f"CREATE INDEX IF NOT EXISTS {idx_name} ON {table}({cols})")
|
||||
|
||||
def _ensure_column_exists(self, connection:sqlite3.Connection, table_name:str, column_name:str, column_type:str) -> None:
|
||||
existing = connection.execute(f"PRAGMA table_info({table_name})").fetchall()
|
||||
obj = connection.execute(
|
||||
"SELECT type FROM sqlite_master WHERE name = ?", (table_name,)
|
||||
).fetchone()
|
||||
|
||||
if obj and obj["type"] == "view":
|
||||
# zstd replaced this table with a view — operate on the underlying compressed table
|
||||
underlying = f"_{table_name}_zstd"
|
||||
exists = connection.execute(
|
||||
"SELECT 1 FROM sqlite_master WHERE name = ? AND type = 'table'", (underlying,)
|
||||
).fetchone()
|
||||
if not exists:
|
||||
return # nothing we can do without the extension
|
||||
actual_table = underlying
|
||||
else:
|
||||
actual_table = table_name
|
||||
|
||||
existing = connection.execute(f"PRAGMA table_info({actual_table})").fetchall()
|
||||
if any(row["name"] == column_name for row in existing):
|
||||
return
|
||||
|
||||
connection.execute(f"ALTER TABLE {table_name} ADD COLUMN {column_name} {column_type}")
|
||||
connection.execute(f"ALTER TABLE {actual_table} ADD COLUMN {column_name} {column_type}")
|
||||
|
||||
def _enable_zstd_compression(self, connection: sqlite3.Connection) -> None:
|
||||
compressed_columns = [
|
||||
|
||||
Reference in New Issue
Block a user