Connection pool can briefly exceed max_size when pooling is disabled under load
维护者通常 1 天内回复
@bewithgaurav 已经在做这个了。
开始于 2026年9月21日。
评估
调研方向
阅读 mssql_python/pybind/connection/connection_pool.cpp,重点关注 ConnectionPool::acquire 的 Phase 3 failure catch、closePools() 和 ConnectionPool::close。跟踪 reservation 和 cleanup 路径,然后检查 PR #678 如何更改了 Connection 的构造。完成的标准是:有一个 regression test 覆盖 failure/disable 交错场景,并且活动的物理连接数永远不超过 max_size。
由索引模型根据 Issue 内容生成。
描述
Describe the bug
The connection pool can transiently exceed its configured max_size when pooling is disabled (or the process begins shutting down) while new physical connections are being opened concurrently. The pool's internal reserved-capacity counter (_current_size) can drift below the true number of live connections, after which the pool opens more physical connections than max_size allows. It is transient and self-corrects as connections close.
Root cause: _current_size is a bare counter with no record of which reservation a given decrement belongs to. The failed-open cleanup runs if (_current_size > 0) --_current_size, while a pool disable/close runs _current_size = 0. If a reset lands between a thread reserving a slot and that same thread's open failing, the thread's decrement cancels a different thread's live reservation instead of its own.
Exception message: none. This is a silent _current_size accounting drift, no exception or stack trace.
To reproduce
This is a rare timing race, so it is not deterministically reproducible from a plain script. It requires a new connection to fail to open at the exact moment pooling is disabled or the process is shutting down. The interleave that produces it, with max_size = 2:
import mssql_python
from concurrent.futures import ThreadPoolExecutor
mssql_python.pooling(max_size=2)
# 1. Thread A calls connect(), reserves a slot -> _current_size = 1,
# then starts opening a new physical connection (outside the pool lock).
# 2. Another thread disables pooling: closePools() sets _current_size = 0.
# 3. Thread B calls connect(), reserves the freed slot -> _current_size = 1.
# 4. Thread A's open fails (handle alloc / network error): its cleanup runs
# --_current_size -> 0, cancelling B's slot instead of A's.
# 5. Thread B's open succeeds, but _current_size now reads 0.
# 6. Further connect() calls see room and open past max_size.
Observable symptom while the count is drifted: more live sessions than the configured cap.
SELECT COUNT(*) FROM sys.dm_exec_sessions WHERE login_name = 'yourapp';
-- transiently returns 3 with max_size = 2
Expected behavior
The number of live physical connections should never exceed max_size, regardless of a connection-open failure racing a pooling disable or process shutdown.
Further technical details
Python version: any
SQL Server version: any
Operating system: any. The race is in the cross-platform C++ pool, not platform specific.
Additional context
Area: mssql_python/pybind/connection/connection_pool.cpp, the Phase 3 failure catch in ConnectionPool::acquire, plus closePools() and ConnectionPool::close.
Regression status: pre-existing for the connect()-failure path. PR #678 moved Connection construction out of _mutex (required to fix the #671 deadlock), which widens the same race to also cover constructor failures such as ODBC env init, handle allocation, and OOM. Raised during review of #678.
Proposed fix: give the pool a generation counter. close()/closePools() bumps it under the lock, a reserver snapshots it at ++_current_size, and the failure cleanup only decrements if the generation still matches. This makes the decrement attributable and closes both the constructor and connect paths. Roughly 6 to 8 lines plus a regression test that forces the interleave.
- 主要语言
- Python
- 星标
- 478
- 派生
- 62
- 平均合并
- 3 天 1 小时
- 30 天内合并 PR
- 27
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
microsoft/mssql-python 的其他 Issue
-
area: bulk-copy
难度 2/5 1-3 小时 新手友好度 70/100
microsoft/mssql-python#843 · 1 条评论 ·
维护者通常 1 天内回复
-
macOS: encrypted connections fail after Homebrew moved the `openssl` alias to `openssl@4`可能已有人在做 @bewithgaurav 于 14 天前认领。 未关闭area: packaging-platform bug triage needed
难度 4/5 3-5 天 新手友好度 55/100
microsoft/mssql-python#826 · 2 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
bulkcopy PyCore login is closed by SQL Server 2019 while ODBC and Go clients succeed (1.14.0)可能重新可做 @bewithgaurav 于 34 天前认领,目前没有进行中的 PR。 未关闭area: bulk-copy bug triage done
microsoft/mssql-python#750 · 4 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
Announcement: mssql-python is coming to Django through mssql-django 2.0可能重新可做 @bewithgaurav 于 41 天前认领,目前没有进行中的 PR。 未关闭announcement
microsoft/mssql-python#748 · 已指派 1 人 ·
维护者通常 1 天内回复
-
Decimal in money range still string-binds on the executemany path可能已有人在做 @bewithgaurav 于 23 天前认领。 未关闭area: data-types bug triage done under development
难度 4/5 3-5 天 新手友好度 48/100
microsoft/mssql-python#745 · 1 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
查看 microsoft/mssql-python 的全部 Issue
相似的 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 68/100
Deltares/imod-python#1940 ·
维护者通常 1 天内回复
-
effort: low prio: low ready
难度 1/5 1 小时以内 新手友好度 82/100
digitalfabrik/integreat-cms#4621 ·
维护者通常 5 天内回复
-
难度 1/5 1 小时以内 新手友好度 88/100
ioos/compliance-checker#1375 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 82/100
维护者通常 1 天内回复
-
netbox status: backlog topic: testing type: housekeeping
难度 2/5 1-3 小时 新手友好度 70/100
netbox-community/netbox#23395 ·
维护者通常 1 天内回复