void RollbackLocal() { Debug.Assert(_connector != null, "No connector"); Debug.Assert(_localTx != null, "No local transaction"); Log.Debug($"Single-phase transaction rollback (localid={_txId})", _connector.Id); var attempt = 0; while (true) { try { _localTx.Rollback(); return; } catch (NpgsqlOperationInProgressException) { // Repeatedly attempts to rollback, to support timeout-triggered rollbacks that occur // while the connection is busy. // This really shouldn't be necessary, but just in case if (attempt++ == MaximumRollbackAttempts) { throw new Exception($"Could not roll back after {MaximumRollbackAttempts} attempts, aborting. Transaction is in an unknown state."); } Log.Warn($"Connection in use while trying to rollback, will cancel and retry (localid={_txId}", _connector.Id); _connector.CancelRequest(); // Cancellations are asynchronous, give it some time Thread.Sleep(500); } } }
/// <summary> /// Cancels and terminates an ongoing operation. Any data already written will be discarded. /// </summary> public void Cancel() { CheckDisposed(); if (CanWrite) { _isDisposed = true; _writeBuf.EndCopyMode(); _writeBuf.Clear(); _connector.SendMessage(new CopyFailMessage()); try { var msg = _connector.ReadMessage(); // The CopyFail should immediately trigger an exception from the read above. _connector.Break(); throw new NpgsqlException("Expected ErrorResponse when cancelling COPY but got: " + msg.Code); } catch (PostgresException e) { if (e.SqlState == "57014") { return; } throw; } } else { _connector.CancelRequest(); } }
/// <summary> /// Cancels an ongoing export. /// </summary> public void Cancel() { _connector.CancelRequest(); }