fix(sqlite): enable WAL for concurrent plugin callback commits

This commit is contained in:
RockChinQ
2026-10-13 11:40:55 +00:00
parent 688e7761e7
commit 48c6a1f536
3 changed files with 42 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
FROM local/langbot-oss-411:688e7761
COPY src/langbot/pkg/persistence/databases/sqlite.py /app/src/langbot/pkg/persistence/databases/sqlite.py
@@ -1,5 +1,6 @@
from __future__ import annotations
import sqlalchemy
import sqlalchemy.ext.asyncio as sqlalchemy_asyncio
from .. import database
@@ -13,3 +14,8 @@ class SQLiteDatabaseManager(database.BaseDatabaseManager):
db_file_path = self.ap.instance_config.data.get('database', {}).get('sqlite', {}).get('path', 'data/langbot.db')
engine_url = f'sqlite+aiosqlite:///{db_file_path}'
self.engine = sqlalchemy_asyncio.create_async_engine(engine_url)
# Reader transactions must not block independent plugin callback commits.
# Configure the persistent journal before serving requests, not per write.
async with self.engine.connect() as connection:
await connection.execute(sqlalchemy.text('PRAGMA journal_mode=WAL'))
await connection.commit()
@@ -0,0 +1,34 @@
from types import SimpleNamespace
import pytest
import sqlalchemy as sa
from langbot.pkg.core import app # noqa: F401 - initialize manager registration before database imports
from langbot.pkg.persistence.databases.sqlite import SQLiteDatabaseManager
pytestmark = [pytest.mark.integration, pytest.mark.asyncio]
async def test_writer_commits_while_independent_reader_is_open(tmp_path):
application = SimpleNamespace(
instance_config=SimpleNamespace(data={'database': {'sqlite': {'path': str(tmp_path / 'concurrency.db')}}})
)
manager = SQLiteDatabaseManager(application)
await manager.initialize()
engine = manager.get_engine()
try:
async with engine.begin() as connection:
await connection.execute(sa.text('CREATE TABLE probe (id INTEGER PRIMARY KEY, value TEXT)'))
await connection.execute(sa.text("INSERT INTO probe VALUES (1, 'old')"))
async with engine.connect() as reader:
await reader.execute(sa.text('BEGIN'))
assert (await reader.execute(sa.text('SELECT value FROM probe'))).scalar_one() == 'old'
async with engine.connect() as writer:
await writer.execute(sa.text('PRAGMA busy_timeout=250'))
await writer.execute(sa.text("UPDATE probe SET value='new' WHERE id=1"))
await writer.commit()
await reader.rollback()
async with engine.connect() as connection:
assert (await connection.execute(sa.text('SELECT value FROM probe'))).scalar_one() == 'new'
finally:
await engine.dispose()