mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-16 13:20:39 -07:00
perf(db): enable WAL and busy timeout for SQLite
The generation worker and request handlers race on the database — enough that orphan-recovery code exists in two places to clean up after 'database is locked' failures. WAL lets readers proceed during a write, synchronous=NORMAL is the recommended pairing, and the 30s sqlite3 timeout waits on a locked database instead of raising immediately.
This commit is contained in:
@@ -3,20 +3,20 @@
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy import create_engine, event
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from .. import config
|
||||
from .migrations import run_migrations
|
||||
from .models import (
|
||||
Base,
|
||||
AudioChannel,
|
||||
Base,
|
||||
EffectPreset,
|
||||
Generation,
|
||||
GenerationVersion,
|
||||
ProfileChannelMapping,
|
||||
VoiceProfile,
|
||||
)
|
||||
from .migrations import run_migrations
|
||||
from .seed import backfill_generation_versions, seed_builtin_presets
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -36,9 +36,22 @@ def init_db() -> None:
|
||||
|
||||
engine = create_engine(
|
||||
f"sqlite:///{_db_path}",
|
||||
connect_args={"check_same_thread": False},
|
||||
# timeout is sqlite3's busy handler: wait up to 30s on a locked
|
||||
# database instead of raising "database is locked" immediately.
|
||||
connect_args={"check_same_thread": False, "timeout": 30},
|
||||
)
|
||||
|
||||
@event.listens_for(engine, "connect")
|
||||
def _set_sqlite_pragmas(dbapi_connection, _connection_record):
|
||||
# WAL lets readers proceed while a writer holds the lock, which is
|
||||
# the main source of lock racing between the generation worker and
|
||||
# request handlers. synchronous=NORMAL is the recommended pairing
|
||||
# (durable across app crashes, fsyncs only on checkpoint).
|
||||
cursor = dbapi_connection.cursor()
|
||||
cursor.execute("PRAGMA journal_mode=WAL")
|
||||
cursor.execute("PRAGMA synchronous=NORMAL")
|
||||
cursor.close()
|
||||
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
run_migrations(engine)
|
||||
@@ -47,7 +60,7 @@ def init_db() -> None:
|
||||
# Create default audio channel if it doesn't exist
|
||||
db = SessionLocal()
|
||||
try:
|
||||
default_channel = db.query(AudioChannel).filter(AudioChannel.is_default == True).first()
|
||||
default_channel = db.query(AudioChannel).filter(AudioChannel.is_default).first()
|
||||
if not default_channel:
|
||||
default_channel = AudioChannel(
|
||||
id=str(uuid.uuid4()),
|
||||
|
||||
Reference in New Issue
Block a user