Sync ExecuteReader doesn't stop after calling command.Cancel() when DB connection is lost
See original GitHub issueBefore submitting
Hello! I am stuck with an issue when using the synchronous API in Npgsql in a situation when DB connection is lost.
The bottom line is that after calling the command interrupt method (NpgsqlCommand.Cancel
), the execution of the NpgsqlCommand.ExecuteReader
method is not interrupted (it will be executed until the Command Timeout has passed).
Steps to reproduce
For example, I can attach this code:
class Program
{
static void Main(string[] args)
{
var connectionString =
"Server=192.168.135.208;Username=npgsql_tests;Password=npgsql_tests;Database=npgsql_tests;Timeout=30;Command Timeout=180";
using (var conn = new NpgsqlConnection(connectionString))
{
conn.Open();
using (var cmd = new NpgsqlCommand("SELECT pg_sleep(150)", conn))
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
try
{
cmd.Cancel();
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
try
{
var reader = cmd.ExecuteReader();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
}
Here is details steps:
- Add breakepoint where cmd.Cancel() called
- Run the program and stop at the breakepoint
- Emulate loss of DB connection (for example, I use
ifdown
interface in my virtual machine, or with iptables rule). - See what after calling
cmd.Cancel()
-cmd.ExecuteReader()
still continues execution and will only be stopped after reaching Command Timeout.
In ExecuterReaderAsync this case works correctly. In spite of the connection breaks, the method (ExecuterReaderAsync) call terminates.
The issue
I expect that after the connection is broken and the command close method is called, there will be an immediate execution stop. Maybe, correct way is: https://github.com/npgsql/npgsql/blob/main/src/Npgsql/NpgsqlConnector.cs#L1489
internal void PerformUserCancellation()
{
_userCancellationRequested = true;
if (AttemptPostgresCancellation && SupportsPostgresCancellation)
{
var cancellationTimeout = Settings.CancellationTimeout;
if (PerformPostgresCancellation() && cancellationTimeout >= 0)
{
if (cancellationTimeout > 0)
{
UserTimeout = cancellationTimeout;
ReadBuffer.Timeout = TimeSpan.FromMilliseconds(cancellationTimeout);
ReadBuffer.Cts.CancelAfter(cancellationTimeout);
}
return;
}
}
UserTimeout = -1;
//ReadBuffer.Timeout = _cancelImmediatelyTimeout;
_socket.Shutdown(SocketShutdown.Both);
ReadBuffer.Cts.Cancel();
}
use _socket.Shutdown
here.
Further technical details
Npgsql version: 5.0.1.1 (or 4.1.3.1) PostgreSQL version: 10.7 (not important) Operating system: Windows 10 (or Fedora 32)
Issue Analytics
- State:
- Created 3 years ago
- Comments:6 (5 by maintainers)
Duplicate of #3187
@vonzshik, OK, thank you.