Skip to main content
Glama
leeguooooo
by leeguooooo
CONNECTION_POOL_FIX.md9.64 kB
# Connection Pool Max-Connections Guard Fix **日期**: 2025-10-15 **优先级**: 🔴 Critical Bug Fix **状态**: ✅ Fixed --- ## 🐛 问题描述 ### 发现的Bug 在 `src/connection_pool.py:158` 附近,当每账户的连接池达到 `max_connections_per_account` 限制时,代码只记录了一条警告日志,然后**立即创建新连接**,完全没有强制执行连接数限制。 ### 原始代码(有bug) ```python except Empty: # 池中没有空闲连接 with self._lock: current_count = self._connection_counts.get(account_id, 0) # 检查是否达到限制 if current_count >= self.max_connections_per_account: logger.warning( f"Max connections ({self.max_connections_per_account}) " f"reached for {account_id}, waiting..." ) # 等待连接可用 # 注意:在生产环境中可能需要添加超时 pass # ❌ BUG: 只是 pass,没有实际等待! # 创建新连接 ❌ 直接创建,绕过了限制! return self._create_new_connection(account_id, account_config) ``` ### 影响 这个bug会导致: 1. **连接数限制失效** - 配置的 `max_connections_per_account=3` 完全无效 2. **IMAP登录泛滥** - 可能创建数十个连接而不是3个 3. **提供商限流** - Gmail等提供商会触发速率限制甚至封禁 4. **资源浪费** - 大量无效连接占用系统资源 5. **连接池失去意义** - 无法有效控制并发连接 --- ## ✅ 解决方案 ### 修复后的逻辑 ```python except Empty: # 池中没有空闲连接,检查是否可以创建新连接 with self._lock: current_count = self._connection_counts.get(account_id, 0) # 如果未达到限制,可以创建新连接 if current_count < self.max_connections_per_account: # ✅ 在锁内创建,避免竞态条件 return self._create_new_connection(account_id, account_config) # ✅ 达到限制,必须等待连接释放 logger.warning( f"Max connections ({self.max_connections_per_account}) " f"reached for {account_id}, waiting for available connection..." ) with self._lock: self.stats['connection_waits'] += 1 # ✅ 阻塞等待连接释放(带超时保护) wait_timeout = 60 # 最多等待60秒 try: pooled_conn = self._pools[account_id].get(timeout=wait_timeout) # 再次检查连接健康状态 if pooled_conn.is_expired(...) or not pooled_conn.is_healthy(): # 如果等到的连接无效,关闭并递归重试 pooled_conn.close() ... return self._acquire_connection(account_id, account_config) # 连接可用 pooled_conn.in_use = True ... return pooled_conn except Empty: # ✅ 等待超时,抛出异常而不是无限创建 with self._lock: self.stats['wait_timeouts'] += 1 error_msg = ( f"Connection pool exhausted for {account_id}: " f"max {self.max_connections_per_account} connections in use, " f"waited {wait_timeout}s with no connection released" ) logger.error(error_msg) raise RuntimeError(error_msg) ``` --- ## 🔧 关键改进 ### 1. 正确的流程控制 **Before (Bug)**: ``` 空闲连接不足 → 检查限制 → 记录警告 → 立即创建新连接 ❌ ``` **After (Fixed)**: ``` 空闲连接不足 → 检查限制 ├─ 未达限制 → 创建新连接 ✅ └─ 已达限制 → 阻塞等待(60s) ├─ 获得连接 → 返回 ✅ └─ 超时 → 抛出异常 ✅ ``` ### 2. 新增统计字段 ```python self.stats = { 'total_created': 0, 'total_reused': 0, 'total_closed': 0, 'health_check_failures': 0, 'connection_waits': 0, # ✅ 新增:等待次数 'wait_timeouts': 0 # ✅ 新增:超时次数 } ``` ### 3. 避免竞态条件 ```python with self._lock: current_count = self._connection_counts.get(account_id, 0) if current_count < self.max_connections_per_account: # ✅ 在锁内创建,防止多线程同时检查通过 return self._create_new_connection(account_id, account_config) ``` ### 4. 超时保护 - **等待上限**: 60秒 - **超时后行为**: 抛出 `RuntimeError` 而不是静默失败 - **清晰的错误信息**: 包含账户ID、限制数、等待时间 ### 5. 健康检查 等待获得的连接可能已过期或不健康: ```python if pooled_conn.is_expired(...) or not pooled_conn.is_healthy(): pooled_conn.close() # 递归重试(此时应该有空位了) return self._acquire_connection(account_id, account_config) ``` --- ## 📊 监控改进 ### 更新 `get_connection_pool_stats` 工具 新增两个关键指标的显示: ``` 🔌 IMAP 连接池统计 • 总创建连接数: 15 • 复用次数: 145 • 已关闭连接数: 3 • 健康检查失败: 1 • 连接等待次数: 5 ✅ 新增 • 等待超时次数: 0 ✅ 新增 ... ⚠️ 警告: 发生了 2 次等待超时! ✅ 新增告警 建议: 增加 max_connections_per_account 或优化连接使用 💡 提示: 发生了 5 次连接等待 ✅ 新增提示 如果频繁等待,考虑增加连接池大小 ``` --- ## 🧪 测试场景 ### 场景1: 正常创建(未达限制) ```python # 配置: max_connections_per_account = 3 # 当前连接数: 2 with pool.get_connection(account_id, config) as conn: # ✅ 立即创建第3个连接,无需等待 ... ``` ### 场景2: 达到限制,等待成功 ```python # 配置: max_connections_per_account = 3 # 当前连接数: 3 (全部在使用中) # 线程1: 请求第4个连接 with pool.get_connection(account_id, config) as conn: # ⏳ 阻塞等待... # 10秒后,线程2释放了一个连接 # ✅ 获得连接,继续执行 ... ``` ### 场景3: 达到限制,等待超时 ```python # 配置: max_connections_per_account = 3 # 当前连接数: 3 (全部在使用中且长时间不释放) try: with pool.get_connection(account_id, config) as conn: # ⏳ 等待60秒... # ❌ 没有连接被释放 ... except RuntimeError as e: # ✅ 抛出异常: "Connection pool exhausted for account_123..." logger.error(f"无法获取连接: {e}") ``` --- ## 📈 预期效果 ### Before (Bug) | 场景 | 配置限制 | 实际连接数 | 结果 | |------|---------|-----------|------| | 正常使用 | 3 | 3-5 | 偶尔超限 ⚠️ | | 高并发 | 3 | 10-20 | 严重超限 ❌ | | Gmail同步 | 3 | 15+ | 触发限流 ❌ | ### After (Fixed) | 场景 | 配置限制 | 实际连接数 | 结果 | |------|---------|-----------|------| | 正常使用 | 3 | ≤3 | 严格遵守 ✅ | | 高并发 | 3 | ≤3 | 排队等待 ✅ | | Gmail同步 | 3 | ≤3 | 无限流 ✅ | --- ## 🔍 代码审查要点 ### 修复前需确认的点 - [x] 检查是否真的没有等待逻辑 - [x] 确认 `pass` 后立即创建连接 - [x] 验证可能导致的影响范围 ### 修复后需验证的点 - [x] 确保在锁内检查并创建(避免竞态) - [x] 验证阻塞等待逻辑正确 - [x] 确认超时后抛出异常而非静默 - [x] 检查递归调用不会导致死循环 - [x] 验证统计信息正确更新 - [x] 确保linter检查通过 --- ## 📝 相关文件 ### 修改文件 1. **`src/connection_pool.py`** - 修复 `_acquire_connection` 方法的等待逻辑 - 新增 `connection_waits` 和 `wait_timeouts` 统计 2. **`src/core/sync_handlers.py`** - 更新 `handle_get_connection_pool_stats` 显示新统计 - 添加等待告警和提示信息 --- ## 🙏 致谢 感谢细心的code review发现了这个关键问题: > "Max-connections guard never enforced (src/connection_pool.py:158): when the per-account pool hits max_connections_per_account, the code only logs "waiting…" and then immediately calls _create_new_connection(). That means we sail past the configured cap and can spam IMAP logins instead of back‑pressure/waiting. Please block until a slot is freed (or throw) before creating a new connection." 这个问题如果不修复,连接池的核心价值(限制连接数)将完全失效。 --- ## ✅ 验证清单 - [x] Bug已修复并测试 - [x] 统计信息已更新 - [x] 监控工具已增强 - [x] Linter检查通过 - [x] 文档已更新 - [ ] 单元测试已添加(待补充) - [ ] 集成测试已验证(待补充) --- ## 🔄 后续建议 1. **添加单元测试** ```python def test_connection_pool_max_connections_enforced(): """测试连接池正确强制执行最大连接数""" pool = IMAPConnectionPool(max_connections_per_account=2) # 占用2个连接 with pool.get_connection(...) as conn1: with pool.get_connection(...) as conn2: # 第3个连接应该等待或超时 with pytest.raises(RuntimeError, match="Connection pool exhausted"): with pool.get_connection(...) as conn3: pass ``` 2. **性能基准测试** - 测试等待逻辑的性能开销 - 验证60秒超时是否合理 3. **可配置超时时间** ```python def __init__(self, ..., connection_wait_timeout: int = 60): self.connection_wait_timeout = connection_wait_timeout ``` --- **修复状态**: ✅ Complete **提交**: 待提交 **优先级**: 🔴 Critical

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/leeguooooo/email-mcp-service'

If you have feedback or need assistance with the MCP directory API, please join our Discord server