Skip to content

Commit cf8d301

Browse files
feihoo87claude
andcommitted
Add SQLite WAL mode support for improved concurrent access
Enable WAL (Write-Ahead Logging) mode for SQLite databases to provide: - Better concurrent read/write performance - Reduced database locking errors - Improved multi-thread/multi-process stability Changes: - Add event listener in LocalStorage to set PRAGMA journal_mode=WAL - Add event listener in SessionManager.from_url() for WAL mode - Update design.md with WAL mode documentation - Update usage.md troubleshooting section - Add test to verify WAL mode is enabled Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 949f800 commit cf8d301

5 files changed

Lines changed: 41 additions & 4 deletions

File tree

docs/storage/design.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,16 @@ port = 6789
480480

481481
## 性能考虑
482482

483+
### SQLite WAL 模式
484+
485+
本地存储使用 SQLite 的 WAL (Write-Ahead Logging) 模式,提供更好的并发性能:
486+
487+
- 允许读取和写入同时进行
488+
- 减少数据库锁定错误
489+
- 提高多线程/多进程访问的稳定性
490+
491+
WAL 模式在存储初始化时自动启用,无需额外配置。
492+
483493
### 写入优化
484494

485495
- Array 使用内存缓冲区 (BUFFER_SIZE=1000)

docs/storage/usage.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -850,10 +850,12 @@ shutil.copytree(storage.base_path, backup_dir)
850850

851851
### 数据库锁定
852852

853-
```python
854-
# 如果出现数据库锁定错误,检查是否有其他进程正在使用
853+
由于使用了 WAL (Write-Ahead Logging) 模式,数据库锁定问题已大大减少。WAL 模式允许读取和写入同时进行,提高了并发性能。
854+
855+
如果出现数据库锁定错误,检查是否有其他进程正在使用:
856+
```bash
855857
# 使用 lsof 检查
856-
# lsof ~/.qulab/storage/storage.db
858+
lsof ~/.qulab/storage/storage.db
857859
```
858860

859861
### 远程连接失败

qulab/storage/local.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from pathlib import Path
66
from typing import TYPE_CHECKING, Iterator, List, Optional, Union
77

8-
from sqlalchemy import create_engine
8+
from sqlalchemy import create_engine, event
99
from sqlalchemy.orm import sessionmaker
1010

1111
from .base import Storage
@@ -36,6 +36,14 @@ def __init__(self, base_path: Union[str, Path], db_url: Optional[str] = None):
3636
self.engine = create_engine(db_url)
3737
self.Session = sessionmaker(bind=self.engine)
3838

39+
# Enable WAL mode for SQLite to improve concurrent access performance
40+
@event.listens_for(self.engine, "connect")
41+
def set_sqlite_wal_mode(dbapi_conn, connection_record):
42+
"""Enable WAL mode for SQLite database."""
43+
import sqlite3
44+
if isinstance(dbapi_conn, sqlite3.Connection):
45+
dbapi_conn.execute("PRAGMA journal_mode=WAL")
46+
3947
# Initialize tables
4048
from .models import Base
4149

qulab/storage/models/base.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ def __init__(self, engine: Engine):
2323
def from_url(cls, url: str) -> "SessionManager":
2424
"""Create a SessionManager from a database URL."""
2525
engine = create_engine(url)
26+
27+
# Enable WAL mode for SQLite to improve concurrent access performance
28+
@event.listens_for(engine, "connect")
29+
def set_sqlite_wal_mode(dbapi_conn, connection_record):
30+
"""Enable WAL mode for SQLite database."""
31+
import sqlite3
32+
if isinstance(dbapi_conn, sqlite3.Connection):
33+
dbapi_conn.execute("PRAGMA journal_mode=WAL")
34+
2635
return cls(engine)
2736

2837
def get_session(self) -> Session:

tests/storage/test_local_storage.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import pytest
44
from pathlib import Path
5+
from sqlalchemy import text
56

67
from qulab.storage.local import LocalStorage, DocumentRef, DatasetRef
78
from qulab.storage.document import Document
@@ -411,3 +412,10 @@ def test_get_latest_document(self, local_storage: LocalStorage):
411412
# Get latest with state filter that matches nothing
412413
not_found_state = local_storage.get_latest_document(name="versioned_doc", state="unknown")
413414
assert not_found_state is None
415+
416+
def test_sqlite_wal_mode_enabled(self, local_storage: LocalStorage):
417+
"""Test that SQLite WAL mode is enabled."""
418+
with local_storage.engine.connect() as conn:
419+
result = conn.execute(text("PRAGMA journal_mode"))
420+
journal_mode = result.scalar()
421+
assert journal_mode.lower() == "wal"

0 commit comments

Comments
 (0)