[D-03][medium] 读连接池没有上限且只保留 2 个空闲连接:群发或批量唤醒时出现 SQLite 连接风暴 #29

Closed
opened 2026-09-30 13:56:57 +08:00 by nixevol · 2 comments
Owner

编号:D-03 严重级:medium 工作线:存储、配置、命令行与部署(internal/store、internal/config、cmd/nixmsg 命令、deploy) 来源:审查 P-14
依赖:C-01 (#32)、C-02 (#33)(推送改为先收集结果再发布、推送并发有上限) 被依赖:无

结论与统一方案

采用审查 P-14 的方案:OpenReader 设 SetMaxIdleConns(16)、SetConnMaxIdleTime(5*time.Minute)、SetMaxOpenConns(64);读连接的 DSN 加 _pragma=query_only(1) 防止误写。

前提是不存在"持有查询结果时又发第二个读查询"或"持有查询结果时做可能长时间阻塞的调用"。message.pushReceipts 目前边遍历查询结果边 PublishDown,会长期占住读连接,需要先由 C-02 改成"先收集、再发布";C-01 的推送并发上限也要小于读池上限。

改动文件

internal/store/db.go。

与其他问题的交互 / 冲突说明

依赖 C-01、C-02,在它们之后合入。

验收与测试

  • 并发 200 个读,db.Read.Stats().OpenConnections 不超过上限。
  • 读连接执行写语句报错。

问题明细(各区审查原文,证据含文件与行号)

以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。解决方案以本 issue 上方的"结论与统一方案"为准;原文里的方案与之不一致时,按上方执行。

[P-14] 读连接池没有上限、只保留 2 个空闲连接:群发或批量唤醒时出现 SQLite 连接风暴

  • 严重级:medium
  • 分类:设计 / 质量
  • 现象与影响:
    • OpenReader 没设连接池参数,默认不限打开数、只保留 2 个空闲连接。
    • WakePush 每个端起一个 goroutine。1000 人群发时,上千个 PushPending 同时读库,会打开上千个 SQLite 连接(每个 3 个文件句柄加页缓存,打开时还要执行 5 条 pragma),用完只留 2 个,下一波又重新打开。
  • 证据:store/db.go:95-109;message/push.go:549-560。
  • 文档依据:DEVELOPMENT 7.2;PRD 第 8 节「1000 人群消息 5 秒内完成推送排队」。
  • 为何不是故意设计:没有记录。
  • 解决方案:SetMaxIdleConns(16)、SetConnMaxIdleTime(5*time.Minute),SetMaxOpenConns 设一个宽裕的上限(例如 64);读连接的 DSN 可以加 _pragma=query_only(1) 防止误写。
  • 改动文件:internal/store/db.go
  • 与其他模块的交互/冲突风险:设上限之前要先确认没有"持有查询结果时又发第二个读查询"或"持有查询结果时做可能长时间阻塞的调用"。message.pushReceipts 就是边遍历查询结果边 PublishDown,可能被 P-7 的慢客户端卡住,长期占着读连接。需要先请 M 线改成"先收集、再发布";P-7 的推送并发上限也要小于读池上限。
  • 需补测试:并发 200 个读,db.Read.Stats().OpenConnections 不超过上限。
  • 置信度:较高(连接数未实测)

复审基线:main 4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。

**编号**:D-03 **严重级**:medium **工作线**:存储、配置、命令行与部署(internal/store、internal/config、cmd/nixmsg 命令、deploy) **来源**:审查 P-14 **依赖**:C-01 (#32)、C-02 (#33)(推送改为先收集结果再发布、推送并发有上限) **被依赖**:无 ### 结论与统一方案 采用审查 P-14 的方案:`OpenReader` 设 `SetMaxIdleConns(16)`、`SetConnMaxIdleTime(5*time.Minute)`、`SetMaxOpenConns(64)`;读连接的 DSN 加 `_pragma=query_only(1)` 防止误写。 前提是不存在"持有查询结果时又发第二个读查询"或"持有查询结果时做可能长时间阻塞的调用"。`message.pushReceipts` 目前边遍历查询结果边 `PublishDown`,会长期占住读连接,需要先由 C-02 改成"先收集、再发布";C-01 的推送并发上限也要小于读池上限。 ### 改动文件 `internal/store/db.go`。 ### 与其他问题的交互 / 冲突说明 依赖 C-01、C-02,在它们之后合入。 ### 验收与测试 - 并发 200 个读,`db.Read.Stats().OpenConnections` 不超过上限。 - 读连接执行写语句报错。 --- ### 问题明细(各区审查原文,证据含文件与行号) > 以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。**解决方案以本 issue 上方的"结论与统一方案"为准**;原文里的方案与之不一致时,按上方执行。 #### [P-14] 读连接池没有上限、只保留 2 个空闲连接:群发或批量唤醒时出现 SQLite 连接风暴 - **严重级**:medium - **分类**:设计 / 质量 - **现象与影响**: - `OpenReader` 没设连接池参数,默认不限打开数、只保留 2 个空闲连接。 - `WakePush` 每个端起一个 goroutine。1000 人群发时,上千个 PushPending 同时读库,会打开上千个 SQLite 连接(每个 3 个文件句柄加页缓存,打开时还要执行 5 条 pragma),用完只留 2 个,下一波又重新打开。 - **证据**:`store/db.go:95-109`;`message/push.go:549-560`。 - **文档依据**:DEVELOPMENT 7.2;PRD 第 8 节「1000 人群消息 5 秒内完成推送排队」。 - **为何不是故意设计**:没有记录。 - **解决方案**:`SetMaxIdleConns(16)`、`SetConnMaxIdleTime(5*time.Minute)`,`SetMaxOpenConns` 设一个宽裕的上限(例如 64);读连接的 DSN 可以加 `_pragma=query_only(1)` 防止误写。 - **改动文件**:`internal/store/db.go` - **与其他模块的交互/冲突风险**:设上限之前要先确认没有"持有查询结果时又发第二个读查询"或"持有查询结果时做可能长时间阻塞的调用"。`message.pushReceipts` 就是边遍历查询结果边 `PublishDown`,可能被 P-7 的慢客户端卡住,长期占着读连接。需要先请 M 线改成"先收集、再发布";P-7 的推送并发上限也要小于读池上限。 - **需补测试**:并发 200 个读,`db.Read.Stats().OpenConnections` 不超过上限。 - **置信度**:较高(连接数未实测) --- <sub>复审基线:main `4059a15`(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。</sub>
nixevol added the P2-mediumlane/storereview-2026-09-30 labels 2026-09-30 13:56:57 +08:00
Author
Owner

D-03 已由 C-03 覆盖,本 issue 不另开补丁。 (#29)

feat/fix-message-c01-c03 提交 a4e51ec5cd 中 OpenReader:SetMaxOpenConns(64)、SetMaxIdleConns(16)、SetConnMaxIdleTime(5m),读 DSN _pragma=query_only(1)。

测试 internal/store/db_pool_test.go TestReaderPoolCapAndQueryOnly:并发 200 读、OpenConnections<=64、读连接执行写语句报错。

前提:C-01 每握手连接一个 push worker(不再每个 WakePush 起 goroutine);C-02 回执先收集再发布。读池上限 64 可挡住群发连接风暴。未合入 main。

D-03 已由 C-03 覆盖,本 issue 不另开补丁。 (#29) `feat/fix-message-c01-c03` 提交 https://git.asio.asia/nixevol/NixMsg/commit/a4e51ec5cd688e451d1e7734b2d3c7036ac46b51 中 `OpenReader`:`SetMaxOpenConns(64)`、`SetMaxIdleConns(16)`、`SetConnMaxIdleTime(5m)`,读 DSN `_pragma=query_only(1)`。 测试 `internal/store/db_pool_test.go` `TestReaderPoolCapAndQueryOnly`:并发 200 读、`OpenConnections<=64`、读连接执行写语句报错。 前提:C-01 每握手连接一个 push worker(不再每个 `WakePush` 起 goroutine);C-02 回执先收集再发布。读池上限 64 可挡住群发连接风暴。未合入 main。
Author
Owner

已合入 origin/main 0c9b459。落地提交 6a65ab8 fix: 按完成时刻分批清理并限制读连接池 (#29)。store 侧见 57c2f70。

已合入 origin/main `0c9b459`。落地提交 `6a65ab8` fix: 按完成时刻分批清理并限制读连接池 (#29)。store 侧见 `57c2f70`。
Sign in to join this conversation.