pgx 回滚时 panic: close of closed channel,检查连续 FATAL 响应
pgx 5.7.0 经 PgBouncer 连接被终止后,清理 pipeline 可能再次关闭同一通知通道。5.9.0 已修复这条路径;保留事务失败处理,别靠 recover 把坏连接放回池。
事务因超时失败,随后调用 Rollback(),进程却出现 panic: close of closed channel。如果堆栈经过 PgConn.receiveMessage、Pipeline.Close 和预备语句缓存清理,先核对 pgx 版本及连接关闭前收到的错误。
pgx 5.7.0 经 PgBouncer 连接 PostgreSQL 的具体场景中,空闲事务到达服务端期限,应用自己的 context 也已经过期,最后的回滚清理触发了这条 panic。不能仅凭“回滚时崩了”就认定应用重复调用了 Go 的 close。
连续 FATAL 让旧清理路径读了两次
PgBouncer 与后端终止连接时,可能有连续的 FATAL ErrorResponse 一起进入客户端缓冲区。第一条错误已经使连接关闭,并关闭用于通知清理结束的 cleanupDone;pipeline 清理继续取下一条缓冲消息时,旧实现又尝试关闭同一个通道,便产生 panic。
因此,给回滚换成未过期的 context,并不能修正驱动内部已经关闭的连接状态。后台 context 也不应被当成无限等待清理的通用方案,应用仍需设置合理的清理期限。
pgx 5.9.0 已正式修复这条 pipeline 关闭路径:接收消息前拒绝已关闭连接,清理遇到缺少预期结果的状态时结束,而不是继续反复读取。当前 5.11.0 保留了相应保护。
更新驱动,同时核对 Go 工具链
采用项目兼容的已含修正的正式版本,并在应用最终构建结果中确认 github.com/jackc/pgx/v5 的版本。5.9.0 起要求 Go 1.25 及以上,当前 5.11.0 的 go.mod 也保留这一要求。仍在较旧工具链上的项目,需要先安排兼容升级,不能只修改一个依赖版本号后忽略构建或运行要求。
保存原 go.mod、go.sum 与部署产物,通过现有依赖管理流程更新。不要直接在模块缓存里改 pgconn.go,这既难以复现构建,也容易在后续依赖下载时丢失。
这次修复针对连续 FATAL 后的状态处理,不保证所有相同 panic 文本都有同一个原因。若已经使用包含修正的版本,继续保留完整堆栈,检查是否有并发使用同一连接或其他通道关闭路径,不能一律通过降级绕过。
回滚失败仍要作为事务失败处理
升级后,服务器已经关闭的连接仍可能返回 conn closed 或其他错误,原先超时的事务不会自动成功。应用应保存最初的执行或提交错误,再记录清理错误,避免最后一次 Rollback 的错误把最初失败完全覆盖。
不要在驱动内部 panic 外简单加 recover,随后继续使用同一连接。recover 不能恢复协议缓冲、预备语句缓存和事务状态。需要由驱动与池正常丢弃坏连接,再按业务规则决定后续请求。
在隔离环境覆盖服务端终止空闲事务、应用 context 过期及清理阶段连续错误,确认进程不再因为上述接收路径崩溃,坏连接被移出池,后续新连接能查询。原事务如果已经在提交阶段失去连接,先核实业务结果再重试;驱动不再 panic,不等于那次提交一定失败或一定成功。


