错误:57014:由于用户请求取消语句。Npgsql

5

我在我的应用程序中遇到了一个“幽灵”问题,在ASP.NET MVC应用程序的特定页面上,每5个请求中就会有一个抛出以下错误:

Npgsql.NpgsqlException: ERROR: 57014: canceling statement due to user request
   at Npgsql.NpgsqlState.<ProcessBackendResponses>d__0.MoveNext()
   at Npgsql.ForwardsOnlyDataReader.GetNextResponseObject(Boolean cleanup)
   at Npgsql.ForwardsOnlyDataReader.GetNextRow(Boolean clearPending)
   at Npgsql.ForwardsOnlyDataReader.Read()
   at Npgsql.NpgsqlCommand.GetReader(CommandBehavior cb)
   ...

在npgsql的github页面上,我发现了以下Bug报告:615
其中写道:
无论Dapper正在发生什么,取消命令时肯定存在竞态条件。部分原因是由于PostgreSQL本身的设计:取消请求是完全"异步"的(它们通过一个不相关的套接字传递,而不是作为要取消的连接的一部分),你不能限制取消只在特定命令执行时生效。换句话说,如果你想取消A命令,在你的取消请求被传送到服务器之前,B命令可能已经开始执行并被取消。
虽然Npgsql 3.0.2已经进行了“更改以使取消更加安全”的修改,但我的当前代码与此版本不兼容,因为需要进行描述 here的迁移。
我的当前解决方法(比较愚蠢):我已经注释了Dapper中的command.Cancel();代码,并且问题似乎已经解决了。
if (reader != null)
                {
                    if (!reader.IsClosed && command != null)
                    {
                        //command.Cancel();
                    }
                    reader.Dispose();
                    reader = null;
                }

这个问题有更好的解决方案吗?其次,除了要记住每次更新 Dapper 的更改外,我还失去了什么当前的修复方法?

配置: NET45, Npgsql 2.2.5, Postgresql 9.3


你为什么要在首次执行查询时取消它?你试图通过这样做解决什么问题? - Craig Ringer
这段代码是我使用的微型ORM Dapper的一部分,它是Dispose方法的一部分。我猜读取器没有正确关闭,这就是调用command.Cancel()的原因。源代码可以在此处找到https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper%20NET40/SqlMapper.cs#L4497 - gyosifov
Dispose的一部分中发送取消语句是可怕的。说真的?那只是一个错误,简单明了。快跑,远离那段代码。你应该向ORM开发人员报告错误...然后最好找到一个更合理的。 - Craig Ringer
谢谢@CraigRinger!我会向开发人员报告这个问题。 - gyosifov
1个回答

1
我发现我的代码没有处理读取器的释放,导致调用了command.Cancel()。只有在未读取每个refcursor时,才会出现这种情况,这仅适用于QueryMultiple方法。
将代码从以下更改:
using (var multipleResults = connection.QueryMultiple("schema.getuserbysocialsecurity", new { socialSecurityNumber }))
{
    var client = multipleResults.Read<Client>().SingleOrDefault();

    if (client != null)
    {
        client.Address = multipleResults.Read<Address>().Single();
    }

    return client;
}

到:

using (var multipleResults = connection.QueryMultiple("schema.getuserbysocialsecurity", new { socialSecurityNumber }))
{
    var client = multipleResults.Read<Client>().SingleOrDefault();
    var address = multipleResults.Read<Address>().SingleOrDefault();

    if (client != null)
    {
        client.Address = address;
    }

    return client;
}

这样做解决了问题,现在读取器被正确地处理,command.Cancel()没有被调用。

希望这能帮助其他人!

更新

npgsql 2.2文档指出:

Npgsql能够请求服务器取消正在进行的命令。为此,请调用NpgsqlCommand的Cancel方法。请注意,另一个线程必须处理请求,因为主线程将被阻塞等待命令完成。此外,由于用户取消,主线程将引发异常。(错误代码为57014。)

我还在Dapper github页面上发布了问题


1
我们在错误使用 Dapper 时遇到了这个问题。具体来说,是因为我们使用 Query 而不是 Execute 来执行 INSERT/UPDATE/DELETE 命令。我认为其他提供程序可能更容忍这种误用情况。 - anon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接