"ERROR: 57014: canceling statement due to user request" Npgsql

"ERROR: 57014: canceling statement due to user request" Npgsql

我在我的应用程序中遇到这个 phantom 问题,在特定页面(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 页面上,我发现了以下错误报告:615

上面写着:

Regardless of what exactly is happening with Dapper, there's definitely a race condition when cancelling commands. Part of this is by design, because of PostgreSQL: cancel requests are totally "asynchronous" (they're delivered via an unrelated socket, not as part of the connection to be cancelled), and you can't restrict the cancellation to take effect only on a specific command. In other words, if you want to cancel command A, by the time your cancellation is delivered command B may already be in progress and it will be cancelled instead.

尽管他们在 Npgsql 3.0.2 中制作了 "changes to hopefully make cancellations much safer",但我当前的代码与此版本不兼容,因为 here 描述了迁移的需要。

我目前的解决方法(愚蠢):我已经在 Dapper 中评论了代码 command.Cancel();,问题似乎已经消失了。

if (reader != null)
                {
                    if (!reader.IsClosed && command != null)
                    {
                        //command.Cancel();
                    }
                    reader.Dispose();
                    reader = null;
                }

有更好的解决方法吗?其次,我在当前修复中丢失了什么(除了每次更新 Dapper 时我都必须记住更改)?

配置: 网络45, Npgsql 2.2.5, PostgreSQL 9.3

我发现为什么我的代码没有处理 reader,导致调用 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;
}

这解决了问题,现在 reader 已正确处理并且 command.Cancel() 未被调用。

希望这对其他人有帮助!

更新

npgsql docs for version 2.2 状态:

Npgsql is able to ask the server to cancel commands in progress. To do this, call the NpgsqlCommand’s Cancel method. Note that another thread must handle the request as the main thread will be blocked waiting for command to finish. Also, the main thread will raise an exception as a result of user cancellation. (The error code is 57014.)

我还在 Dapper github 页面上发布了一个 issue