@@ -108,6 +108,32 @@ def _make_async_url(url: str) -> str:
108108# Alembic, peewee migration, health checks)
109109# ============================================================
110110
111+ # Shared SQLite connection/close handlers, used by both sync and async engines
112+ def _sqlite_on_connect (dbapi_connection , connection_record ):
113+ cursor = dbapi_connection .cursor ()
114+ if DATABASE_ENABLE_SQLITE_WAL :
115+ cursor .execute ('PRAGMA journal_mode=WAL' )
116+ # synchronous=NORMAL is safe with WAL: durable on app crash, may roll back
117+ # last transactions on OS crash or power loss; acceptable for most web apps.
118+ cursor .execute ('PRAGMA synchronous=NORMAL' )
119+ cursor .execute ('PRAGMA journal_size_limit=67108864' ) # Cap WAL file at 64 MB
120+ cursor .execute ('PRAGMA cache_size=-32768' ) # 32 MB page cache
121+ cursor .execute ('PRAGMA temp_store=MEMORY' )
122+ cursor .execute ('PRAGMA busy_timeout=5000' ) # Retry up to 5 s on lock contention
123+ else :
124+ cursor .execute ('PRAGMA journal_mode=DELETE' )
125+ cursor .close ()
126+
127+
128+ def _sqlite_on_checkin (dbapi_connection , connection_record ):
129+ if dbapi_connection is None :
130+ return
131+ cursor = dbapi_connection .cursor ()
132+ cursor .execute ('PRAGMA analysis_limit=400' ) # Bound analysis work per checkin
133+ cursor .execute ('PRAGMA optimize' ) # Update query planner statistics
134+ cursor .close ()
135+
136+
111137# Handle SQLCipher URLs
112138if SQLALCHEMY_DATABASE_URL .startswith ('sqlite+sqlcipher://' ):
113139 database_password = os .environ .get ('DATABASE_PASSWORD' )
@@ -154,16 +180,8 @@ def create_sqlcipher_connection():
154180
155181elif 'sqlite' in SQLALCHEMY_DATABASE_URL :
156182 engine = create_engine (SQLALCHEMY_DATABASE_URL , connect_args = {'check_same_thread' : False })
157-
158- def on_connect (dbapi_connection , connection_record ):
159- cursor = dbapi_connection .cursor ()
160- if DATABASE_ENABLE_SQLITE_WAL :
161- cursor .execute ('PRAGMA journal_mode=WAL' )
162- else :
163- cursor .execute ('PRAGMA journal_mode=DELETE' )
164- cursor .close ()
165-
166- event .listen (engine , 'connect' , on_connect )
183+ event .listen (engine , 'connect' , _sqlite_on_connect )
184+ event .listen (engine , 'checkin' , _sqlite_on_checkin )
167185else :
168186 if isinstance (DATABASE_POOL_SIZE , int ):
169187 if DATABASE_POOL_SIZE > 0 :
@@ -212,14 +230,8 @@ def get_session():
212230 ASYNC_SQLALCHEMY_DATABASE_URL ,
213231 connect_args = {'check_same_thread' : False },
214232 )
215-
216- if DATABASE_ENABLE_SQLITE_WAL :
217-
218- @event .listens_for (async_engine .sync_engine , 'connect' )
219- def _set_sqlite_wal (dbapi_connection , connection_record ):
220- cursor = dbapi_connection .cursor ()
221- cursor .execute ('PRAGMA journal_mode=WAL' )
222- cursor .close ()
233+ event .listen (async_engine .sync_engine , 'connect' , _sqlite_on_connect )
234+ event .listen (async_engine .sync_engine , 'checkin' , _sqlite_on_checkin )
223235else :
224236 if isinstance (DATABASE_POOL_SIZE , int ):
225237 if DATABASE_POOL_SIZE > 0 :
0 commit comments