Npgsql GSS 加密回退后,查询报 ManualResetEventSlim 已释放
Npgsql 的特定 GSS 协商断开回退路径可能过早释放 connector 事件,10.0.3包含修补,不能将所有同名异常归为这项问题。
连接已经打开,简单查询可能也成功,但连接归还池后再次使用,却在 ResetCancellation() 抛出 ObjectDisposedException,对象名是 ManualResetEventSlim。在 Windows/.NET 10 的受影响组合中,一个具体触发路径是 GSS 加密协商失败后重新连接,不能把所有同名异常都归成这项缺陷。
需要关注的组合是:GSS 加密模式允许回退,目标端在收到 GSS 协商请求时直接关闭连接,驱动随后重新建立连接继续工作。正常返回“不支持 GSS”的响应,并不等同于这个断开重试分支。
受影响路径在中途执行了完整清理,释放 ReadingPrependedMessagesMRE,但仍复用同一个 connector 进行连接重试。之后连接池复用、读取预置响应时又访问这个事件,才暴露“已经释放”的错误。高延迟或某种代理可能让症状显现,却不能单凭它们确定根因。
核对具体版本和回退路径
Npgsql 10.0.3 包含相应修补:可继续重试的阶段使用较轻的清理,保留仍会使用的资源;最终失败或真正释放 connector 时再完整清理。应使用包含修补且与项目提供程序兼容的版本。
dotnet list path/to/App.csproj package --include-transitive
EF Core 项目要看实际解析到的 Npgsql 包,不能只看 Npgsql.EntityFrameworkCore.PostgreSQL 的版本。错误堆栈出现在设计期迁移,也不代表迁移 SQL 本身有问题;同一底层连接路径可以影响运行期查询。
在测试环境中,保留原有 GSS、TLS 与池配置,使用独立、顺序执行的命令检查:
for (var i = 0; i < 2; i++)
{
await using var conn = await dataSource.OpenConnectionAsync();
await using var cmd = new NpgsqlCommand("SELECT 1", conn);
var value = await cmd.ExecuteScalarAsync();
Console.WriteLine(value);
}
dataSource 使用应用已有的配置。这段检查覆盖连接归还池后再借用的行为,但只有实际进入 GSS 断开回退分支,才能验证该触发条件;不触发协商回退的一次成功连接,不能证明缺陷已被覆盖。
不把关闭加密或并发查询当作修复
不要为掩盖异常而全局关闭加密配置、停用连接池或不断回退旧包。先确认正式修补作用于运行中的驱动,再核对连接协商日志和后续查询。日志应帮助识别协商分支,不必打印完整含密码的连接字符串。
同一连接不支持任意并发执行多个命令。用这种不受支持的程序“稳定复现”,不能证明真实的顺序查询就是同一问题。其他 ManualResetEventSlim 等待、取消回调死锁,或调用方在命令执行中释放连接,也应按各自堆栈处理。
验收至少包括协商回退后完成查询、归还连接再借用,以及原有正常连接路径。修补针对资源在重试阶段被过早释放,不是承诺所有取消、代理或网络错误都可以自动恢复。


