Skip to content

Commit

Permalink
Move "KILL QUERY" fix into MySqlSesssion.
Browse files Browse the repository at this point in the history
Track waiting for a pending cancellation as part of MySqlSession.m_state (instead of a separate property on MySqlCommand) and execute "DO SLEEP(0);" directly on the session.
  • Loading branch information
bgrainger committed Apr 22, 2017
1 parent fee93b1 commit 13cfd38
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 14 deletions.
1 change: 0 additions & 1 deletion src/MySqlConnector/MySqlClient/MySqlCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public override void Prepare()

public override string CommandText { get; set; }
public override int CommandTimeout { get; set; }
public bool IsCanceled { get; internal set; }

public override CommandType CommandType
{
Expand Down
9 changes: 0 additions & 9 deletions src/MySqlConnector/MySqlClient/MySqlDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,15 +288,6 @@ private void DoClose()
if (!connection.BufferResultSets)
connection.Session.FinishQuerying();

if (Command.IsCanceled)
{
// KILL QUERY will kill a subsequent query if the command it was intended to cancel has already completed.
// In order to handle this case, we issue a dummy query to catch the QueryInterrupted exception.
// See https://bugs.mysql.com/bug.php?id=45679
var killClearCommand = new MySqlCommand("DO SLEEP(0);", connection);
killClearCommand.ExecuteNonQuery();
}

Command.ReaderClosed();
if ((m_behavior & CommandBehavior.CloseConnection) != 0)
{
Expand Down
30 changes: 26 additions & 4 deletions src/MySqlConnector/Serialization/MySqlSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ public void DoCancel(MySqlCommand commandToCancel, MySqlCommand killCommand)
// blocking the other thread for an extended duration.
killCommand.CommandTimeout = 3;
killCommand.ExecuteNonQuery();

commandToCancel.IsCanceled = true;
}
}

Expand Down Expand Up @@ -125,9 +123,30 @@ public void SetActiveReader(MySqlDataReader dataReader)

public void FinishQuerying()
{
bool clearConnection = false;
lock (m_lock)
{
VerifyState(State.Querying, State.CancelingQuery);
if (m_state == State.CancelingQuery)
{
m_state = State.ClearingPendingCancellation;
clearConnection = true;
}
}

if (clearConnection)
{
// KILL QUERY will kill a subsequent query if the command it was intended to cancel has already completed.
// In order to handle this case, we issue a dummy query that will consume the pending cancellation.
// See https://bugs.mysql.com/bug.php?id=45679
var payload = new PayloadData(new ArraySegment<byte>(PayloadUtilities.CreateEofStringPayload(CommandKind.Query, "DO SLEEP(0);")));
SendAsync(payload, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
payload = ReceiveReplyAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
OkPayload.Create(payload);
}

lock (m_lock)
{
VerifyState(State.Querying, State.ClearingPendingCancellation);
m_state = State.Connected;
m_activeReader = null;
m_activeCommand = null;
Expand Down Expand Up @@ -313,7 +332,7 @@ private void VerifyConnected()
{
if (m_state == State.Closed)
throw new ObjectDisposedException(nameof(MySqlSession));
if (m_state != State.Connected && m_state != State.Querying && m_state != State.CancelingQuery && m_state != State.Closing)
if (m_state != State.Connected && m_state != State.Querying && m_state != State.CancelingQuery && m_state != State.ClearingPendingCancellation && m_state != State.Closing)
throw new InvalidOperationException("MySqlSession is not connected.");
}
}
Expand Down Expand Up @@ -669,6 +688,9 @@ private enum State
// The session is connected to a server and the active query is being cancelled.
CancelingQuery,

// A cancellation is pending on the server and needs to be cleared.
ClearingPendingCancellation,

// The session is closing.
Closing,

Expand Down
22 changes: 22 additions & 0 deletions tests/SideBySide/CancelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,28 @@ value varchar(45)

await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
}

using (var cmd = m_database.Connection.CreateCommand())
{
cmd.CommandText = "select value from cancel_completed_command where id = 1;";
var value = (string) await cmd.ExecuteScalarAsync();
Assert.Equal("value", value);
}
}

[Fact]
public void ImplicitCancelWithDapper()
{
m_database.Connection.Execute(@"drop table if exists cancel_completed_command;
create table cancel_completed_command(id integer not null primary key, value text null);");

// a query that returns 0 fields will cause Dapper to cancel the command
m_database.Connection.Query<int>("insert into cancel_completed_command(id, value) values (1, null);");

m_database.Connection.Execute("update cancel_completed_command set value = 'value' where id = 1;");

var value = m_database.Connection.Query<string>(@"select value from cancel_completed_command where id = 1").FirstOrDefault();
Assert.Equal("value", value);
}

[UnbufferedResultSetsFact]
Expand Down

0 comments on commit 13cfd38

Please sign in to comment.