Skip to content

fix(bay): 改善 SQLite 并发访问 - #27

Draft
RhoninSeiei wants to merge 2 commits into
AstrBotDevs:mainfrom
RhoninSeiei:fix/sqlite-concurrency
Draft

fix(bay): 改善 SQLite 并发访问#27
RhoninSeiei wants to merge 2 commits into
AstrBotDevs:mainfrom
RhoninSeiei:fix/sqlite-concurrency

Conversation

@RhoninSeiei

@RhoninSeiei RhoninSeiei commented Jul 15, 2026

Copy link
Copy Markdown

修改内容

  • SQLite 连接超时设置为 30 秒。
  • 每个 SQLite 连接设置 busy_timeout=30000journal_mode=WALsynchronous=FULL
  • 其他数据库驱动保持原有连接参数。

问题原因

Bay 的调度器、垃圾回收任务和 API 请求会并发访问同一个 SQLite 文件。默认回滚日志与较短等待时间容易在写事务重叠时返回 database is locked

验证

  • 专项单元测试:1 passed
  • 生产组合分支单元测试:525 passed
  • Ruff:通过
  • 实际运行参数:journal_mode=walsynchronous=2busy_timeout=30000

Summary by Sourcery

Improve SQLite configuration for concurrent Bay tasks while keeping other database backends unchanged.

Bug Fixes:

  • Reduce SQLite database is locked errors by increasing connection timeout and configuring write-ahead logging with appropriate sync settings.

Enhancements:

  • Introduce centralized SQLite connection configuration applied on engine connect events.
  • Derive SQLite-specific async engine connect_args from settings while leaving non-SQLite drivers with existing parameters.

Tests:

  • Add an async unit test verifying SQLite connections use WAL journal mode, FULL synchronous setting, and a 30-second busy timeout.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request configures persistent SQLite connections for concurrent tasks by setting a busy timeout, enabling WAL journal mode, and configuring synchronous mode, along with adding a unit test to verify these settings. The review feedback recommends checking if WAL mode is successfully enabled to prevent silent failures on unsupported filesystems, and suggests changing the synchronous mode from FULL to NORMAL to optimize concurrent write performance, with corresponding updates to the unit test.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +29 to +37
def _configure_sqlite_connection(dbapi_connection, _connection_record) -> None:
"""Configure persistent SQLite connections for concurrent Bay tasks."""
cursor = dbapi_connection.cursor()
try:
cursor.execute(f"PRAGMA busy_timeout={_SQLITE_BUSY_TIMEOUT_MS}")
cursor.execute("PRAGMA journal_mode=WAL")
cursor.execute("PRAGMA synchronous=FULL")
finally:
cursor.close()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

在通过 PRAGMA journal_mode=WAL 启用 WAL 模式时,如果数据库文件位于不支持共享内存(mmap)的文件系统(例如 NFS、CIFS/SMB 等网络共享卷,这在 Kubernetes 环境中很常见)或只读文件系统上,SQLite 会静默失败,并返回之前的日志模式(如 delete),而不会抛出异常。

为了避免这种静默失败并便于在容器化/Kubernetes 环境中排查问题,强烈建议检查执行 PRAGMA journal_mode=WAL 后返回的实际模式,并在其不为 wal 时记录警告日志。

此外,对于 WAL 模式,SQLite 官方强烈推荐使用 PRAGMA synchronous=NORMAL(值为 1)而不是 FULL(值为 2)。在 WAL 模式下,NORMAL 已经能够完全保证数据库不会损坏,且仅在极罕见的双重电力故障时可能丢失最后一两个事务。相比之下,FULL 会在每次事务提交时强制同步 WAL 文件到磁盘,这会显著降低并发写入性能,从而增加在高并发场景下触发 database is locked 的概率。

Suggested change
def _configure_sqlite_connection(dbapi_connection, _connection_record) -> None:
"""Configure persistent SQLite connections for concurrent Bay tasks."""
cursor = dbapi_connection.cursor()
try:
cursor.execute(f"PRAGMA busy_timeout={_SQLITE_BUSY_TIMEOUT_MS}")
cursor.execute("PRAGMA journal_mode=WAL")
cursor.execute("PRAGMA synchronous=FULL")
finally:
cursor.close()
def _configure_sqlite_connection(dbapi_connection, _connection_record) -> None:
"""Configure persistent SQLite connections for concurrent Bay tasks."""
cursor = dbapi_connection.cursor()
try:
cursor.execute(f"PRAGMA busy_timeout={_SQLITE_BUSY_TIMEOUT_MS}")
cursor.execute("PRAGMA journal_mode=WAL")
journal_mode = cursor.fetchone()[0]
if journal_mode.lower() != "wal":
logger.warning(
"sqlite.wal_mode_failed",
requested="WAL",
actual=journal_mode,
detail="WAL mode might not be supported on this filesystem (e.g., network shares).",
)
cursor.execute("PRAGMA synchronous=NORMAL")
finally:
cursor.close()

Comment on lines +32 to +34
assert journal_mode == "wal"
assert synchronous == 2
assert busy_timeout == 30_000

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

由于建议将 SQLite 的 synchronous 模式调整为官方推荐的 NORMAL(值为 1),我们需要同步更新此处的单元测试断言,以确保测试能够顺利通过。

Suggested change
assert journal_mode == "wal"
assert synchronous == 2
assert busy_timeout == 30_000
assert journal_mode == "wal"
assert synchronous == 1
assert busy_timeout == 30_000

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant