问题概述
最新 main 上,Proxy 的连接缓存使用一个全局 mutex 保护所有租户和所有 CN 的缓存状态,但 connCache.Pop 在持有该 mutex 时同步执行后端网络 I/O:
SET CONNECTION ID:sc.ExecStmt(...)
- 错误/过期连接清理:
sc.Quit()
如果任意一个缓存 CN 连接不响应,当前 Pop 会一直持有全局 mutex,导致其他租户的 Push/Pop/Count 以及 Proxy 关闭时的 connCache.Close 全部一起阻塞。
connCache.Push 同样在持锁状态下执行 resetSessionFunc RPC,也存在最长数秒的全局串行阻塞。
验证基线
- 仓库:
matrixorigin/matrixone
- 分支:最新
main
- Commit:
824090c2e61351972cc16f866a57d52c7c2cab6e
- Commit 时间:
2026-07-17T01:10:07+00:00
- Commit 标题:
perf(hashmap): allocate int iterator buffers lazily (#25796)
- 环境:macOS arm64,Go
go1.26.2
测试前再次执行 git fetch origin main,确认 origin/main 未前进。
白盒测试过程
临时新增 TestConnCacheCloseDoesNotWaitForBlockedPopNetworkIO:
- 创建真实
connCache,注入无网络行为的 reset/auth 配置。
- Push 一个 fake
ServerConn 到 tenant-a。
- fake
ServerConn.ExecStmt 在 channel 上阻塞,模拟缓存 CN 已失去响应。
- 并发调用
cache.Pop("tenant-a", ...),确认已经进入 ExecStmt。
- 再并发调用
cache.Close()。
- 期望:Close 不应被一个后端连接的网络 I/O 永久阻塞,应能标记 cache closed 并主动清理/取消连接。
- 实际:100ms 后 Close 仍未返回;只有测试解除
ExecStmt 阻塞、让 Pop 释放全局 mutex 后,Close 才能完成。
- 断言前完成所有 channel/goroutine/连接清理,测试失败不会遗留后台任务。
核心场景:
go func() {
popDone <- cache.Pop("tenant-a", 1, nil, nil)
}()
<-sc.entered // Pop 正持有 cache.mu 并进入后端 ExecStmt
go func() {
closeDone <- cache.Close()
}()
select {
case <-closeDone:
closedPromptly = true
case <-time.After(100 * time.Millisecond):
}
实际执行与结果
普通模式
go test ./pkg/proxy \
-run '^TestConnCacheCloseDoesNotWaitForBlockedPopNetworkIO$' \
-count=20 -v -timeout=60s
结果:FAIL 20/20。
Race 模式
go test -race ./pkg/proxy \
-run '^TestConnCacheCloseDoesNotWaitForBlockedPopNetworkIO$' \
-count=5 -v -timeout=60s
结果:FAIL 5/5;没有 race detector 告警,失败原因始终是 Close 等待 cache mutex。
代表性输出:
--- FAIL: TestConnCacheCloseDoesNotWaitForBlockedPopNetworkIO (0.10s)
Error: Should be true
Messages: connCache.Close is blocked by network I/O performed under the cache mutex
根因分析
pkg/proxy/conn_cache.go:
Pop 在第 376 行获取 c.mu,第 377 行 defer unlock。
- 第 434-437 行在锁内调用
sc.ExecStmt(SET CONNECTION ID);serverConn.ExecStmt 的读取路径没有 caller context。
- 第 444、472 行错误/过期路径也在锁内调用
sc.Quit()。
Close 需要获取同一个 c.mu 才能设置 closed 并取出连接,因此被网络 I/O 阻塞。
- 代码在 unhealthy-CN 分支已经明确先从缓存移除连接、临时 unlock、再执行
sc.Quit(),说明网络清理应在临界区外完成;正常/错误/过期分支尚未采用同样模式。
Push 第 330-357 行也在持有全局 mutex 时调用 reset-session RPC。
影响
- 单个失去响应的缓存 CN 连接可以阻塞所有租户的连接缓存操作。
- 新登录连接可能在 Proxy cache mutex 上排队,与故障租户/故障 CN 相互影响。
- Proxy 关闭可能永久等待,影响重启、滚动升级和故障恢复。
- reset-session RPC 在 Push 锁内执行,即使最终超时也会造成全局串行停顿。
- 该问题属于 Proxy 普通连接缓存路径,与 2PC 无关。
建议修复
- 在锁内只完成连接的选取、占用/移除和状态校验。
- 将
resetSessionFunc、ExecStmt、Quit 等网络调用移动到临界区外。
- 网络调用结束后重新加锁,并检查 cache 是否已关闭、候选连接是否仍属于本次操作。
- 给网络操作贯穿可取消 context/timeout,确保 Proxy Close 可以主动中止。
- 增加跨租户并发与 Close 场景回归测试,并覆盖 race 模式。
查重
已搜索 connCache Pop mutex ExecStmt、proxy connection cache network I/O lock、connCache Close blocked、cached connection hang all tenants,未发现相同 issue。
问题概述
最新
main上,Proxy 的连接缓存使用一个全局 mutex 保护所有租户和所有 CN 的缓存状态,但connCache.Pop在持有该 mutex 时同步执行后端网络 I/O:SET CONNECTION ID:sc.ExecStmt(...)sc.Quit()如果任意一个缓存 CN 连接不响应,当前
Pop会一直持有全局 mutex,导致其他租户的 Push/Pop/Count 以及 Proxy 关闭时的connCache.Close全部一起阻塞。connCache.Push同样在持锁状态下执行resetSessionFuncRPC,也存在最长数秒的全局串行阻塞。验证基线
matrixorigin/matrixonemain824090c2e61351972cc16f866a57d52c7c2cab6e2026-07-17T01:10:07+00:00perf(hashmap): allocate int iterator buffers lazily (#25796)go1.26.2测试前再次执行
git fetch origin main,确认origin/main未前进。白盒测试过程
临时新增
TestConnCacheCloseDoesNotWaitForBlockedPopNetworkIO:connCache,注入无网络行为的 reset/auth 配置。ServerConn到tenant-a。ServerConn.ExecStmt在 channel 上阻塞,模拟缓存 CN 已失去响应。cache.Pop("tenant-a", ...),确认已经进入ExecStmt。cache.Close()。ExecStmt阻塞、让 Pop 释放全局 mutex 后,Close 才能完成。核心场景:
实际执行与结果
普通模式
结果:FAIL 20/20。
Race 模式
结果:FAIL 5/5;没有 race detector 告警,失败原因始终是 Close 等待 cache mutex。
代表性输出:
根因分析
pkg/proxy/conn_cache.go:Pop在第 376 行获取c.mu,第 377 行 defer unlock。sc.ExecStmt(SET CONNECTION ID);serverConn.ExecStmt的读取路径没有 caller context。sc.Quit()。Close需要获取同一个c.mu才能设置 closed 并取出连接,因此被网络 I/O 阻塞。sc.Quit(),说明网络清理应在临界区外完成;正常/错误/过期分支尚未采用同样模式。Push第 330-357 行也在持有全局 mutex 时调用 reset-session RPC。影响
建议修复
resetSessionFunc、ExecStmt、Quit等网络调用移动到临界区外。查重
已搜索
connCache Pop mutex ExecStmt、proxy connection cache network I/O lock、connCache Close blocked、cached connection hang all tenants,未发现相同 issue。