-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
fix: add SQLite busy_timeout and WAL autocheckpoint to prevent CPU 100% epoll busy wait (Closes #8056) #8111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -59,6 +59,7 @@ def __init__(self, db_path: str) -> None: | |||||||||||||||||||||||||||||||
| async def initialize(self) -> None: | ||||||||||||||||||||||||||||||||
| """Initialize the SQLite database and create the documents table if it doesn't exist.""" | ||||||||||||||||||||||||||||||||
| await self.connect() | ||||||||||||||||||||||||||||||||
| await self._apply_pragma() | ||||||||||||||||||||||||||||||||
| async with self.engine.begin() as conn: # type: ignore | ||||||||||||||||||||||||||||||||
|
Comment on lines
+62
to
63
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||
| # Create tables using SQLModel | ||||||||||||||||||||||||||||||||
| await conn.run_sync(BaseDocModel.metadata.create_all) | ||||||||||||||||||||||||||||||||
|
|
@@ -197,13 +198,23 @@ async def connect(self) -> None: | |||||||||||||||||||||||||||||||
| self.DATABASE_URL, | ||||||||||||||||||||||||||||||||
| echo=False, | ||||||||||||||||||||||||||||||||
| future=True, | ||||||||||||||||||||||||||||||||
| connect_args={"timeout": 30}, | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Clarify whether both You’re now setting the lock wait in two places: Suggested implementation: self.DATABASE_URL,
echo=False,
future=True,
# timeout is in seconds; aligned with PRAGMA busy_timeout (30_000 ms)
connect_args={"timeout": 30},
) await conn.execute(text("PRAGMA journal_mode=WAL"))
await conn.execute(text("PRAGMA synchronous=NORMAL"))
# 30_000 ms = 30 seconds; aligned with connect_args["timeout"]
await conn.execute(text("PRAGMA busy_timeout=30000")) |
||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| self.async_session_maker = sessionmaker( | ||||||||||||||||||||||||||||||||
| self.engine, # type: ignore | ||||||||||||||||||||||||||||||||
| class_=AsyncSession, | ||||||||||||||||||||||||||||||||
| expire_on_commit=False, | ||||||||||||||||||||||||||||||||
| ) # type: ignore | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| async def _apply_pragma(self) -> None: | ||||||||||||||||||||||||||||||||
| """Apply SQLite PRAGMAs for performance and concurrency.""" | ||||||||||||||||||||||||||||||||
| async with self.engine.begin() as conn: | ||||||||||||||||||||||||||||||||
| await conn.execute(text("PRAGMA journal_mode=WAL")) | ||||||||||||||||||||||||||||||||
| await conn.execute(text("PRAGMA synchronous=NORMAL")) | ||||||||||||||||||||||||||||||||
| await conn.execute(text("PRAGMA busy_timeout=5000")) | ||||||||||||||||||||||||||||||||
| await conn.execute(text("PRAGMA wal_autocheckpoint=500")) | ||||||||||||||||||||||||||||||||
| await conn.commit() | ||||||||||||||||||||||||||||||||
|
Comment on lines
+209
to
+216
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 此新方法存在以下问题:
Suggested change
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @asynccontextmanager | ||||||||||||||||||||||||||||||||
| async def get_session(self): | ||||||||||||||||||||||||||||||||
| """Context manager for database sessions.""" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -42,6 +42,7 @@ def __init__(self, db_path: str | None = None) -> None: | |||||||||||||||||
| echo=False, | ||||||||||||||||||
| pool_pre_ping=True, | ||||||||||||||||||
| pool_recycle=3600, | ||||||||||||||||||
| connect_args={"timeout": 30}, | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| # 创建会话工厂 | ||||||||||||||||||
|
|
@@ -68,15 +69,13 @@ async def initialize(self) -> None: | |||||||||||||||||
| async with self.engine.begin() as conn: | ||||||||||||||||||
| # 创建所有知识库相关表 | ||||||||||||||||||
| await conn.run_sync(BaseKBModel.metadata.create_all) | ||||||||||||||||||
|
|
||||||||||||||||||
| # 配置 SQLite 性能优化参数 | ||||||||||||||||||
| # 配置 SQLite 参数以优化并发性能 | ||||||||||||||||||
| await conn.execute(text("PRAGMA journal_mode=WAL")) | ||||||||||||||||||
| await conn.execute(text("PRAGMA synchronous=NORMAL")) | ||||||||||||||||||
| await conn.execute(text("PRAGMA cache_size=20000")) | ||||||||||||||||||
| await conn.execute(text("PRAGMA busy_timeout=5000")) | ||||||||||||||||||
| await conn.execute(text("PRAGMA wal_autocheckpoint=500")) | ||||||||||||||||||
| await conn.execute(text("PRAGMA temp_store=MEMORY")) | ||||||||||||||||||
|
Comment on lines
+76
to
78
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
| await conn.execute(text("PRAGMA mmap_size=134217728")) | ||||||||||||||||||
| await conn.execute(text("PRAGMA optimize")) | ||||||||||||||||||
| await conn.commit() | ||||||||||||||||||
|
|
||||||||||||||||||
| self.inited = True | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PRAGMA busy_timeout=5000(5秒) 与BaseDatabase构造函数中设置的timeout=30(30秒) 不一致。执行此 PRAGMA 会覆盖连接级别的设置。建议统一使用 30000ms 以保持一致性,确保在高负载下有足够的等待时间避免SQLITE_BUSY错误。此外,这套 SQLite 优化参数在多个文件(
sqlite.py,document_storage.py,kb_db_sqlite.py)中重复出现。根据通用规则,建议将其重构为共享的助手函数以提高代码复用性和可维护性。References