Skip to content

fix: dispose database engine on shutdown#8650

Open
KBVsent wants to merge 1 commit into
AstrBotDevs:masterfrom
KBVsent:fix/db-engine-dispose-shutdown
Open

fix: dispose database engine on shutdown#8650
KBVsent wants to merge 1 commit into
AstrBotDevs:masterfrom
KBVsent:fix/db-engine-dispose-shutdown

Conversation

@KBVsent
Copy link
Copy Markdown
Contributor

@KBVsent KBVsent commented Jun 7, 2026

After #7724 was reverted (#8638), the SQLite engines use SQLAlchemy's default connection pool again instead of NullPool. Each pooled aiosqlite connection owns a background worker thread, and aiosqlite never marks these threads as daemon, so they inherit the main thread's non-daemon status. core_lifecycle.stop() terminates every manager but never disposes the main database engine, so the pool's idle connection threads stay alive and block threading._shutdown() at interpreter exit. As a result, a single Ctrl+C hangs instead of shutting down cleanly. NullPool had been masking this by closing each connection (and its thread) right after use.

Modifications / 改动点

  • astrbot/core/core_lifecycle.py: dispose self.db.engine at the end of stop() so the connection pool closes idle connections and their aiosqlite worker threads exit, letting the process terminate. The knowledge base engine is already disposed in its own close(); this aligns the main engine.
  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

Before:
HapiGo_2026-06-07_07 34 32
After:
image


Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Bug Fixes:

  • Ensure database engine disposal on shutdown to prevent lingering aiosqlite worker threads from blocking interpreter exit.

@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend labels Jun 7, 2026
Copy link
Copy Markdown
Contributor

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

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 introduces database engine connection pool disposal during the application shutdown process in core_lifecycle.py. The reviewer suggested adding defensive checks to verify the existence of self.db and its engine attribute before calling dispose(), which prevents potential AttributeError exceptions in uninitialized or testing environments.

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 thread astrbot/core/core_lifecycle.py
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • Consider whether self.db.engine.dispose() is actually awaitable in your setup; in vanilla SQLAlchemy’s AsyncEngine it’s a synchronous method, so wrapping it in await could raise a TypeError and you may instead want to call it synchronously or via asyncio.to_thread if it can block.
  • It may be safer to guard the dispose call with a null/attribute check (e.g., if self.db and getattr(self.db, "engine", None):) to avoid errors in shutdown paths where the database might not have been fully initialized.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider whether `self.db.engine.dispose()` is actually awaitable in your setup; in vanilla SQLAlchemy’s `AsyncEngine` it’s a synchronous method, so wrapping it in `await` could raise a `TypeError` and you may instead want to call it synchronously or via `asyncio.to_thread` if it can block.
- It may be safer to guard the dispose call with a null/attribute check (e.g., `if self.db and getattr(self.db, "engine", None):`) to avoid errors in shutdown paths where the database might not have been fully initialized.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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

Labels

area:core The bug / feature is about astrbot's core, backend size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant