/// <summary> /// Put a pooled connector into the pool queue. /// </summary> /// <param name="Connector">Connector to pool</param> private void UngetPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) { ConnectorQueue Queue; // Find the queue. if (!PooledConnectors.TryGetValue(Connection.ConnectionString, out Queue) || Queue == null) { return; // Queue may be emptied by connection problems. See ClearPool below. } Connector.ProvideClientCertificatesCallback -= Connection.ProvideClientCertificatesCallbackDelegate; Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; Queue.UseCount--; if (!Connector.IsInitialized) { if (Connector.Transaction != null) { Connector.Transaction.Cancel(); } Connector.Close(); } else { if (Connector.Transaction != null) { try { Connector.Transaction.Rollback(); } catch { Connector.Close(); } } } if (Connector.State == ConnectionState.Open && (Thread.CurrentThread.ThreadState & (ThreadState.Aborted | ThreadState.AbortRequested)) == 0) { // Release all resources associated with this connector. Connector.ReleaseResources(); Queue.Enqueue(Connector); } }
/// <summary> /// Put a pooled connector into the pool queue. /// </summary> /// <param name="Connector">Connector to pool</param> private void UngetPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) { ConnectorQueue Queue; // Find the queue. Queue = (ConnectorQueue)PooledConnectors[Connector.ConnectionString.ToString()]; if (Queue == null) { return; // Queue may be emptied by connection problems. See ClearPool below. } Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; Queue.UseCount--; if (!Connector.IsInitialized) { if (Connector.Transaction != null) { Connector.Transaction.Cancel(); } Connector.Close(); } else { if (Connector.Transaction != null) { try { Connector.Transaction.Rollback(); } catch { Connector.Close() ; } } } if (Connector.State == System.Data.ConnectionState.Open) { // Release all resources associated with this connector. Connector.ReleaseResources(); Queue.Enqueue(Connector); } }
///<summary> /// This method is responsible to handle all protocol messages sent from the backend. /// It holds all the logic to do it. /// To exchange data, it uses a Mediator object from which it reads/writes information /// to handle backend requests. /// </summary> /// internal IEnumerable <IServerResponseObject> ProcessBackendResponsesEnum(NpgsqlConnector context) { try { // Flush buffers to the wire. context.Stream.Flush(); // Process commandTimeout behavior. if ((context.Mediator.BackendCommandTimeout > 0) && (!CheckForContextSocketAvailability(context, SelectMode.SelectRead))) { // If timeout occurs when establishing the session with server then // throw an exception instead of trying to cancel query. This helps to prevent loop as // CancelRequest will also try to stablish a connection and sends commands. if (!((this is NpgsqlStartupState || this is NpgsqlConnectedState))) { try { context.CancelRequest(); ProcessAndDiscardBackendResponses(context); } catch (Exception) { } // We should have gotten an error from CancelRequest(). Whether we did or not, what we // really have is a timeout exception, and that will be less confusing to the user than // "operation cancelled by user" or similar, so whatever the case, that is what we'll throw. // Changed message again to report about the two possible timeouts: connection or command // as the establishment timeout only was confusing users when the timeout was a command timeout. } throw new NpgsqlException(resman.GetString("Exception_ConnectionOrCommandTimeout")); } switch (context.BackendProtocolVersion) { case ProtocolVersion.Version2: return(ProcessBackendResponses_Ver_2(context)); case ProtocolVersion.Version3: return(ProcessBackendResponses_Ver_3(context)); default: throw new NpgsqlException(resman.GetString("Exception_UnknownProtocol")); } } catch (ThreadAbortException) { try { context.CancelRequest(); context.Close(); } catch {} throw; } }
private static void ClearQueue(ConnectorQueue Queue) { if (Queue == null) { return; } lock (Queue) { while (Queue.Available.Count > 0) { NpgsqlConnector connector = Queue.Available.Dequeue(); try { connector.Close(); } catch { // Maybe we should log something here to say we got an exception while closing connector? } } //Clear the busy list so that the current connections don't get re-added to the queue Queue.Busy.Clear(); } }
protected override void Dispose(bool disposing) { if (!disposing) { mContext.Close(); mContext = null; } base.Dispose(disposing); }
/// <summary> /// Close the connector. /// </summary> /// <param name="Connection"></param> /// <param name="Connector">Connector to release</param> private static void UngetNonPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) { Connector.ProvideClientCertificatesCallback -= Connection.ProvideClientCertificatesCallbackDelegate; if (Connector.Transaction != null) { Connector.Transaction.Cancel(); } Connector.Close(); }
internal void Release(NpgsqlConnector connector) { // If Clear/ClearAll has been been called since this connector was first opened, // throw it away. if (connector.ClearCounter < _clearCounter) { try { connector.Close(); } catch (Exception e) { Log.Warn("Exception while closing outdated connector", e, connector.Id); } lock (this) Busy--; return; } if (connector.IsBroken) { lock (this) Busy--; return; } connector.Reset(); lock (this) { // If there are any pending open attempts in progress hand the connector off to // them directly. while (Waiting.Count > 0) { var tcs = Waiting.Dequeue(); // Some attempts may be in the queue but in cancelled state, since they've already timed out. // Simply dequeue these and move on. if (tcs.Task.IsCanceled) { continue; } // We have a pending open attempt. "Complete" it, handing off the connector. // We do this in another thread because we don't want to execute the continuation here. Task.Run(() => tcs.SetResult(connector)); return; } Idle.Push(connector); Busy--; EnsurePruningTimerState(); Contract.Assert(Idle.Count <= _max); } }
/// <summary> /// Close the connector. /// </summary> /// <param name="Connector">Connector to release</param> private void UngetNonPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) { Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; if (Connector.Transaction != null) { Connector.Transaction.Cancel(); } Connector.Close(); }
internal IEnumerable <IServerResponseObject> ProcessExistingBackendResponses(NpgsqlConnector context) { try { return(ProcessBackendResponses_Ver_3(context)); } catch (ThreadAbortException) { try { context.CancelRequest(); context.Close(); } catch { } throw; } }
void Dispose() { if (_isDisposed) { return; } Debug.Assert(_transaction != null, "No transaction"); Debug.Assert(_connector != null, "No connector"); Log.CleaningUpResourceManager(_connector.Id, _txId); if (_localTx != null) { _localTx.Dispose(); _localTx = null; } if (_connector.Connection != null) { _connector.Connection.EnlistedTransaction = null; } else { // We're here for connections which were closed before their TransactionScope completes. // These need to be closed now. if (_connector.Settings.Pooling) { ConnectorPool pool; lock (PoolManager.Pools) { var found = PoolManager.Pools.TryGetValue(_connector.ConnectionString, out pool); Debug.Assert(found); } pool.RemovePendingEnlistedConnector(_connector, _transaction); pool.Release(_connector); } else { _connector.Close(); } } _connector = null; _transaction = null; _isDisposed = true; }
private void ClearQueue(ConnectorQueue Queue) { if (Queue == null) { return; } while (Queue.Count > 0) { NpgsqlConnector connector = (NpgsqlConnector)Queue.Dequeue(); try { connector.Close(); } catch { // Maybe we should log something here to say we got an exception while closing connector? } } }
#pragma warning disable CS8625 void Dispose() { if (_isDisposed) { return; } Log.Trace($"Cleaning up resource manager (localid={_txId}", _connector.Id); if (_localTx != null) { _localTx.Dispose(); _localTx = null; } if (_connector.Connection != null) { _connector.Connection.EnlistedTransaction = null; } else { // We're here for connections which were closed before their TransactionScope completes. // These need to be closed now. if (_connector.Settings.Pooling) { var found = PoolManager.TryGetValue(_connector.ConnectionString, out var pool); Debug.Assert(found); pool !.TryRemovePendingEnlistedConnector(_connector, _transaction); pool.Return(_connector); } else { _connector.Close(); } } _connector = null !; _transaction = null !; _isDisposed = true; }
/// <summary> /// Close the connector. /// </summary> /// <param name="Connection"></param> /// <param name="Connector">Connector to release</param> private static void UngetNonPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) { Connector.ProvideClientCertificatesCallback -= Connection.ProvideClientCertificatesCallbackDelegate; Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; if (Connector.Transaction != null) { Connector.Transaction.Cancel(); } Connector.Close(); }
/// <summary> /// Put a pooled connector into the pool queue. /// </summary> /// <param name="Connector">Connector to pool</param> private void UngetPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) { ConnectorQueue queue; // Find the queue. if (!PooledConnectors.TryGetValue(Connection.ConnectionString, out queue) || queue == null) { Connector.Close(); // Release connection to postgres return; // Queue may be emptied by connection problems. See ClearPool below. } Connector.ProvideClientCertificatesCallback -= Connection.ProvideClientCertificatesCallbackDelegate; Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; bool inQueue = queue.Busy.ContainsKey(Connector); queue.Busy.Remove(Connector); if (!Connector.IsInitialized) { if (Connector.Transaction != null) { Connector.Transaction.Cancel(); } Connector.Close(); } else { if (Connector.Transaction != null) { try { Connector.Transaction.Rollback(); } catch { Connector.Close(); } } } if (Connector.State == ConnectionState.Open) { //If thread is good if ((Thread.CurrentThread.ThreadState & (ThreadState.Aborted | ThreadState.AbortRequested)) == 0) { // Release all resources associated with this connector. try { Connector.ReleaseResources(); } catch (Exception) { //If the connector fails to release its resources then it is probably broken, so make sure we don't add it to the queue. // Usually it already won't be in the queue as it would of broken earlier inQueue = false; } if (inQueue) { queue.Available.Enqueue(Connector); } else { Connector.Close(); } } else { //Thread is being aborted, this connection is possibly broken. So kill it rather than returning it to the pool Connector.Close(); } } }
/// <summary> /// Find an available pooled connector in the non-shared pool, or create /// a new one if none found. /// </summary> private NpgsqlConnector GetPooledConnector(NpgsqlConnection Connection) { ConnectorQueue Queue; NpgsqlConnector Connector = null; // Try to find a queue. Queue = (ConnectorQueue)PooledConnectors[Connection.ConnectionString.ToString()]; if (Queue == null) { Queue = new ConnectorQueue(); Queue.ConnectionLifeTime = Connection.ConnectionLifeTime; Queue.MinPoolSize = Connection.MinPoolSize; PooledConnectors[Connection.ConnectionString.ToString()] = Queue; } // Fix queue use count. Use count may be dropped below zero if Queue was cleared and there were connections open. if (Queue.UseCount < 0) { Queue.UseCount = 0; } if (Queue.Count > 0) { // Found a queue with connectors. Grab the top one. // Check if the connector is still valid. Connector = (NpgsqlConnector)Queue.Dequeue(); Queue.UseCount++; } else if (Queue.Count + Queue.UseCount < Connection.MaxPoolSize) { Connector = CreateConnector(Connection); Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; try { Connector.Open(); } catch { try { Connector.Close(); } catch {} throw; } Queue.UseCount++; } // Meet the MinPoolSize requirement if needed. if (Connection.MinPoolSize > 0) { while (Queue.Count + Queue.UseCount < Connection.MinPoolSize) { NpgsqlConnector Spare = CreateConnector(Connection); Spare.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; Spare.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; Spare.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; Spare.Open(); Spare.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Spare.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Spare.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; Queue.Enqueue(Spare); } } return(Connector); }
internal void Release(NpgsqlConnector connector) { // If Clear/ClearAll has been been called since this connector was first opened, // throw it away. if (connector.ClearCounter < _clearCounter) { try { connector.Close(); } catch (Exception e) { Log.Warn("Exception while closing outdated connector", e, connector.Id); } lock (this) Busy--; return; } if (connector.IsBroken) { lock (this) Busy--; return; } connector.Reset(); lock (this) { // If there are any pending open attempts in progress hand the connector off to // them directly. while (Waiting.Count > 0) { var waitingOpenAttempt = Waiting.Dequeue(); var tcs = waitingOpenAttempt.TaskCompletionSource; // Some attempts may be in the queue but in cancelled state, since they've already timed out. // Simply dequeue these and move on. if (tcs.Task.IsCanceled) { continue; } // We have a pending open attempt. "Complete" it, handing off the connector. if (waitingOpenAttempt.IsAsync) { // If the waiting open attempt is asynchronous (i.e. OpenAsync()), we can't simply // call SetResult on its TaskCompletionSource, since it would execute the open's // continuation in our thread (the closing thread). Instead we schedule the completion // to run in the TP Task.Run(() => tcs.SetResult(connector)); } else { tcs.SetResult(connector); } return; } Idle.Push(connector); Busy--; EnsurePruningTimerState(); Contract.Assert(Idle.Count <= _max); } }
/* * /// <summary> * /// Find an available shared connector in the shared pool, or create * /// a new one if none found. * /// </summary> * private NpgsqlConnector GetSharedConnector(NpgsqlConnection Connection) * { * // To be implemented * * return null; * } */ /// <summary> /// Put a pooled connector into the pool queue. /// </summary> /// <param name="Connection">Connection <paramref name="Connector"/> is leased to.</param> /// <param name="Connector">Connector to pool</param> private void UngetConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) { ConnectorQueue queue; // Find the queue. // As we are handling all possible queues, we have to lock everything... lock (locker) { PooledConnectors.TryGetValue(Connection.ConnectionString, out queue); } if (queue == null) { Connector.Close(); // Release connection to postgres return; // Queue may be emptied by connection problems. See ClearPool below. } Connector.ProvideClientCertificatesCallback -= Connection.ProvideClientCertificatesCallbackDelegate; Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; Connector.ValidateRemoteCertificateCallback -= Connection.ValidateRemoteCertificateCallbackDelegate; /*bool inQueue = false; * * lock (queue) * { * inQueue = queue.Busy.ContainsKey(Connector); * queue.Busy.Remove(Connector); * } */ if (!Connector.IsInitialized) { if (Connector.Transaction != null) { Connector.Transaction.Cancel(); } Connector.Close(); } else { if (Connector.Transaction != null) { try { Connector.Transaction.Rollback(); } catch { Connector.Close(); } } } bool inQueue = queue.Busy.ContainsKey(Connector); if (Connector.State == ConnectionState.Open) { //If thread is good if ((Thread.CurrentThread.ThreadState & (ThreadState.Aborted | ThreadState.AbortRequested)) == 0) { // Release all resources associated with this connector. try { Connector.ReleaseResources(); } catch (Exception) { //If the connector fails to release its resources then it is probably broken, so make sure we don't add it to the queue. // Usually it already won't be in the queue as it would of broken earlier inQueue = false; Connector.Close(); } } else { //Thread is being aborted, this connection is possibly broken. So kill it rather than returning it to the pool inQueue = false; Connector.Close(); } } // Check if Connector should return to the queue of available connectors. If not, this connector is invalid and should // only be removed from the busy queue which effectvely removes it from the pool. if (inQueue) { lock (queue) { queue.Busy.Remove(Connector); queue.Available.Enqueue(Connector); } } else { lock (queue) { queue.Busy.Remove(Connector); } } }
/// <summary> /// Find an available pooled connector in the non-shared pool, or create /// a new one if none found. /// </summary> private NpgsqlConnector GetPooledConnector(NpgsqlConnection Connection) { ConnectorQueue Queue; NpgsqlConnector Connector = null; // Try to find a queue. if (!PooledConnectors.TryGetValue(Connection.ConnectionString, out Queue)) { Queue = new ConnectorQueue(); Queue.ConnectionLifeTime = Connection.ConnectionLifeTime; Queue.MinPoolSize = Connection.MinPoolSize; PooledConnectors[Connection.ConnectionString] = Queue; } if (Queue.Available.Count > 0) { // Found a queue with connectors. Grab the top one. // Check if the connector is still valid. Connector = Queue.Available.Dequeue(); if (!Connector.IsValid()) { Connector.Close(); return(GetPooledConnector(Connection)); //Try again } Queue.Busy.Add(Connector, null); } else if (Queue.Available.Count + Queue.Busy.Count < Connection.MaxPoolSize) { Connector = CreateConnector(Connection); Connector.ProvideClientCertificatesCallback += Connection.ProvideClientCertificatesCallbackDelegate; Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; try { Connector.Open(); } catch { Connector.Close(); throw; } Queue.Busy.Add(Connector, null); } // Meet the MinPoolSize requirement if needed. if (Connection.MinPoolSize > 0) { while (Queue.Available.Count + Queue.Busy.Count < Connection.MinPoolSize) { NpgsqlConnector Spare = CreateConnector(Connection); Spare.ProvideClientCertificatesCallback += Connection.ProvideClientCertificatesCallbackDelegate; Spare.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; Spare.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; Spare.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; Spare.Open(); Spare.ProvideClientCertificatesCallback -= Connection.ProvideClientCertificatesCallbackDelegate; Spare.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Spare.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Spare.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; Queue.Available.Enqueue(Spare); } } return(Connector); }
/// <summary> /// Releases a connector, possibly back to the pool for future use. /// </summary> /// <remarks> /// Pooled connectors will be put back into the pool if there is room. /// </remarks> /// <param name="connection">Connection to which the connector is leased.</param> /// <param name="connector">The connector to release.</param> internal void ReleaseConnector(NpgsqlConnection connection, NpgsqlConnector connector) { Contract.Requires(connector.IsReady || connector.IsClosed || connector.IsBroken); ConnectorQueue queue; // Find the queue. // As we are handling all possible queues, we have to lock everything... lock (locker) { PooledConnectors.TryGetValue(connection.ConnectionString, out queue); } if (queue == null) { connector.Close(); // Release connection to postgres return; // Queue may be emptied by connection problems. See ClearPool below. } /*bool inQueue = false; * * lock (queue) * { * inQueue = queue.Busy.ContainsKey(Connector); * queue.Busy.Remove(Connector); * } */ bool inQueue = queue.Busy.ContainsKey(connector); if (connector.IsBroken || connector.IsClosed) { if (connector.InTransaction) { connector.ClearTransaction(); } connector.Close(); inQueue = false; } else { Contract.Assert(connector.IsReady); //If thread is good if ((Thread.CurrentThread.ThreadState & (ThreadState.Aborted | ThreadState.AbortRequested)) == 0) { // Release all resources associated with this connector. try { connector.Reset(); } catch { connector.Close(); inQueue = false; } } else { //Thread is being aborted, this connection is possibly broken. So kill it rather than returning it to the pool inQueue = false; connector.Close(); } } // Check if Connector should return to the queue of available connectors. If not, this connector is invalid and should // only be removed from the busy queue which effectvely removes it from the pool. if (inQueue) { lock (queue) { queue.Busy.Remove(connector); queue.Available.Enqueue(connector); } } else { lock (queue) { queue.Busy.Remove(connector); } } connector.ProvideClientCertificatesCallback = null; connector.UserCertificateValidationCallback = null; }
/// <summary> /// Put a pooled connector into the pool queue. /// </summary> /// <param name="Connector">Connector to pool</param> private void UngetPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) { ConnectorQueue Queue; // Find the queue. Queue = (ConnectorQueue)PooledConnectors[Connector.ConnectionString.ToString()]; if (Queue == null) return; // Queue may be emptied by connection problems. See ClearPool below. Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; Queue.UseCount--; if (! Connector.IsInitialized) { if (Connector.Transaction != null) { Connector.Transaction.Cancel(); } Connector.Close(); } else { if (Connector.Transaction != null) { try { Connector.Transaction.Rollback(); } catch { Connector.Close() ; } } } if (Connector.State == System.Data.ConnectionState.Open) { // Release all resources associated with this connector. Connector.ReleaseResources(); Queue.Enqueue(Connector); } }
///<summary> /// This method is responsible to handle all protocol messages sent from the backend. /// It holds all the logic to do it. /// To exchange data, it uses a Mediator object from which it reads/writes information /// to handle backend requests. /// </summary> /// internal IEnumerable<IServerResponseObject> ProcessBackendResponsesEnum(NpgsqlConnector context) { try { // Process commandTimeout behavior. if ((context.Mediator.CommandTimeout > 0) && (!context.Socket.Poll(1000000*context.Mediator.CommandTimeout, SelectMode.SelectRead))) { // If timeout occurs when establishing the session with server then // throw an exception instead of trying to cancel query. This helps to prevent loop as CancelRequest will also try to stablish a connection and sends commands. if (!((this is NpgsqlStartupState || this is NpgsqlConnectedState || context.CancelRequestCalled))) { try { context.CancelRequest(); foreach (IServerResponseObject obj in ProcessBackendResponsesEnum(context)) { if (obj is IDisposable) { (obj as IDisposable).Dispose(); } } } catch(Exception ex) { } //We should have gotten an error from CancelRequest(). Whether we did or not, what we //really have is a timeout exception, and that will be less confusing to the user than //"operation cancelled by user" or similar, so whatever the case, that is what we'll throw. // Changed message again to report about the two possible timeouts: connection or command as the establishment timeout only was confusing users when the timeout was a command timeout. } throw new NpgsqlException(resman.GetString("Exception_ConnectionOrCommandTimeout")); } switch (context.BackendProtocolVersion) { case ProtocolVersion.Version2: return ProcessBackendResponses_Ver_2(context); case ProtocolVersion.Version3: return ProcessBackendResponses_Ver_3(context); default: throw new NpgsqlException(resman.GetString("Exception_UnknownProtocol")); } } catch(ThreadAbortException) { try { context.CancelRequest(); context.Close(); } catch {} throw; } }
/// <summary> /// Find an available pooled connector in the non-shared pool, or create /// a new one if none found. /// </summary> private NpgsqlConnector GetPooledConnector(NpgsqlConnection Connection) { ConnectorQueue Queue; NpgsqlConnector Connector = null; // Try to find a queue. if (!PooledConnectors.TryGetValue(Connection.ConnectionString, out Queue)) { Queue = new ConnectorQueue(); Queue.ConnectionLifeTime = Connection.ConnectionLifeTime; Queue.MinPoolSize = Connection.MinPoolSize; PooledConnectors[Connection.ConnectionString] = Queue; } // Fix queue use count. Use count may be dropped below zero if Queue was cleared and there were connections open. if (Queue.UseCount < 0) { Queue.UseCount = 0; } if (Queue.Count > 0) { // Found a queue with connectors. Grab the top one. // Check if the connector is still valid. Connector = Queue.Dequeue(); /*try * { * Connector.TestConnector(); * Connector.RequireReadyForQuery = true; * } * catch //This connector is broken! * { * try * { * Connector.Close(); * } * catch * { * try * { * Connector.Stream.Close(); * } * catch * { * } * } * return GetPooledConnector(Connection); //Try again * }*/ if (!Connector.IsValid()) { try { Connector.Close(); } catch { try { Connector.Stream.Close(); } catch { } } return(GetPooledConnector(Connection)); //Try again } Queue.UseCount++; } else if (Queue.Count + Queue.UseCount < Connection.MaxPoolSize) { Connector = CreateConnector(Connection); Connector.ProvideClientCertificatesCallback += Connection.ProvideClientCertificatesCallbackDelegate; Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; try { Connector.Open(); } catch { try { Connector.Close(); } catch { } throw; } Queue.UseCount++; } // Meet the MinPoolSize requirement if needed. if (Connection.MinPoolSize > 0) { while (Queue.Count + Queue.UseCount < Connection.MinPoolSize) { NpgsqlConnector Spare = CreateConnector(Connection); Spare.ProvideClientCertificatesCallback += Connection.ProvideClientCertificatesCallbackDelegate; Spare.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; Spare.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; Spare.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; Spare.Open(); Spare.ProvideClientCertificatesCallback -= Connection.ProvideClientCertificatesCallbackDelegate; Spare.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Spare.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Spare.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; Queue.Enqueue(Spare); } } return(Connector); }
/// <summary> /// Releases a connector, possibly back to the pool for future use. /// </summary> /// <remarks> /// Pooled connectors will be put back into the pool if there is room. /// </remarks> /// <param name="connection">Connection to which the connector is leased.</param> /// <param name="connector">The connector to release.</param> internal void ReleaseConnector(NpgsqlConnection connection, NpgsqlConnector connector) { Contract.Requires(connector.IsReady || connector.IsClosed || connector.IsBroken); ConnectorQueue queue; // Find the queue. // As we are handling all possible queues, we have to lock everything... lock (locker) { PooledConnectors.TryGetValue(connection.ConnectionString, out queue); } if (queue == null) { connector.Close(); // Release connection to postgres return; // Queue may be emptied by connection problems. See ClearPool below. } /*bool inQueue = false; lock (queue) { inQueue = queue.Busy.ContainsKey(Connector); queue.Busy.Remove(Connector); } */ bool inQueue = queue.Busy.ContainsKey(connector); if (connector.IsBroken || connector.IsClosed) { if (connector.InTransaction) { connector.ClearTransaction(); } connector.Close(); inQueue = false; } else { Contract.Assert(connector.IsReady); //If thread is good if ((Thread.CurrentThread.ThreadState & (ThreadState.Aborted | ThreadState.AbortRequested)) == 0) { // Release all resources associated with this connector. try { connector.Reset(); } catch { connector.Close(); inQueue = false; } } else { //Thread is being aborted, this connection is possibly broken. So kill it rather than returning it to the pool inQueue = false; connector.Close(); } } // Check if Connector should return to the queue of available connectors. If not, this connector is invalid and should // only be removed from the busy queue which effectvely removes it from the pool. if (inQueue) lock (queue) { queue.Busy.Remove(connector); queue.Available.Enqueue(connector); } else lock (queue) { queue.Busy.Remove(connector); } connector.ProvideClientCertificatesCallback = null; connector.UserCertificateValidationCallback = null; }
/// <summary> /// Creates another connector and sends a cancel request through it for this connector. /// </summary> internal void CancelRequest() { var cancelConnector = new NpgsqlConnector(_settings, false); try { // Get a raw connection, possibly SSL... cancelConnector.RawOpen(cancelConnector.ConnectionTimeout*1000); // Cancel current request. cancelConnector.SendCancelRequest(BackEndKeyData); } finally { cancelConnector.Close(); } }
/* /// <summary> /// Find an available shared connector in the shared pool, or create /// a new one if none found. /// </summary> private NpgsqlConnector GetSharedConnector(NpgsqlConnection Connection) { // To be implemented return null; } */ /// <summary> /// Put a pooled connector into the pool queue. /// </summary> /// <param name="Connector">Connector to pool</param> private void UngetConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) { ConnectorQueue queue; // Find the queue. // As we are handling all possible queues, we have to lock everything... lock (locker) { PooledConnectors.TryGetValue(Connection.ConnectionString, out queue); } if (queue == null) { Connector.Close(); // Release connection to postgres return; // Queue may be emptied by connection problems. See ClearPool below. } Connector.ProvideClientCertificatesCallback -= Connection.ProvideClientCertificatesCallbackDelegate; Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; Connector.ValidateRemoteCertificateCallback -= Connection.ValidateRemoteCertificateCallbackDelegate; bool inQueue = false; lock (queue) { inQueue = queue.Busy.ContainsKey(Connector); queue.Busy.Remove(Connector); } if (!Connector.IsInitialized) { if (Connector.Transaction != null) { Connector.Transaction.Cancel(); } Connector.Close(); } else { if (Connector.Transaction != null) { try { Connector.Transaction.Rollback(); } catch { Connector.Close(); } } } if (Connector.State == ConnectionState.Open) { //If thread is good if ((Thread.CurrentThread.ThreadState & (ThreadState.Aborted | ThreadState.AbortRequested)) == 0) { // Release all resources associated with this connector. try { Connector.ReleaseResources(); } catch (Exception) { //If the connector fails to release its resources then it is probably broken, so make sure we don't add it to the queue. // Usually it already won't be in the queue as it would of broken earlier inQueue = false; } if (inQueue) lock (queue) { queue.Available.Enqueue(Connector); } else Connector.Close(); } else { //Thread is being aborted, this connection is possibly broken. So kill it rather than returning it to the pool Connector.Close(); } } }
internal IEnumerable<IServerResponseObject> ProcessExistingBackendResponses(NpgsqlConnector context) { try { return ProcessBackendResponses_Ver_3(context); } catch (ThreadAbortException) { try { context.CancelRequest(); context.Close(); } catch { } throw; } }
internal void Release(NpgsqlConnector connector) { // If Clear/ClearAll has been been called since this connector was first opened, // throw it away. if (connector.ClearCounter < _clearCounter) { try { connector.Close(); } catch (Exception e) { Log.Warn("Exception while closing outdated connector", e, connector.Id); } lock (this) DecrementBusy(); Counters.SoftDisconnectsPerSecond.Increment(); Counters.NumberOfPooledConnections.Decrement(); return; } if (connector.IsBroken) { lock (this) DecrementBusy(); Counters.NumberOfPooledConnections.Decrement(); return; } connector.Reset(); lock (this) { // If there are any pending open attempts in progress hand the connector off to // them directly. while (_waiting.Count > 0) { var waitingOpenAttempt = _waiting.Dequeue(); var tcs = waitingOpenAttempt.TaskCompletionSource; // Some attempts may be in the queue but in cancelled state, since they've already timed out. // Simply dequeue these and move on. if (tcs.Task.IsCanceled) { continue; } // We have a pending open attempt. "Complete" it, handing off the connector. if (waitingOpenAttempt.IsAsync) { // If the waiting open attempt is asynchronous (i.e. OpenAsync()), we can't simply // call SetResult on its TaskCompletionSource, since it would execute the open's // continuation in our thread (the closing thread). Instead we schedule the completion // to run in the TP // We copy tcs2 and especially connector2 to avoid allocations caused by the closure, see // http://stackoverflow.com/questions/41507166/closure-heap-allocation-happening-at-start-of-method var tcs2 = tcs; var connector2 = connector; Task.Run(() => { if (!tcs2.TrySetResult(connector2)) { // Race condition: the waiter timed out between our IsCanceled check above and here // Recursively call Release again, this will dequeue another open attempt and retry. Debug.Assert(tcs2.Task.IsCanceled); Release(connector2); } }); } else { tcs.SetResult(connector); } return; } Idle.Push(connector); DecrementBusy(); EnsurePruningTimerState(); Debug.Assert(Idle.Count <= _max); } }
///<summary> /// This method is responsible to handle all protocol messages sent from the backend. /// It holds all the logic to do it. /// To exchange data, it uses a Mediator object from which it reads/writes information /// to handle backend requests. /// </summary> /// internal IEnumerable<IServerResponseObject> ProcessBackendResponsesEnum(NpgsqlConnector context) { try { // Flush buffers to the wire. context.Stream.Flush(); // Process commandTimeout behavior. // We will give an extra 5 seconds to context.Mediator.CommandTimeout // because we'd prefer to receive a timeout error from PG // than to be forced to start a new connection and send a cancel request. // The result is that a timeout could take 5 seconds too long to occur, but if everything // is healthy, that shouldn't happen. if ((context.Mediator.BackendCommandTimeout > 0) && (!context.Stream.WaitAvailable(TimeSpan.FromSeconds(context.Mediator.BackendCommandTimeout + 5)))) { // If timeout occurs when establishing the session with server then // throw an exception instead of trying to cancel query. This helps to prevent loop as // CancelRequest will also try to stablish a connection and sends commands. if (!((this is NpgsqlStartupState || this is NpgsqlConnectedState))) { try { context.CancelRequest(); ProcessAndDiscardBackendResponses(context); } catch(Exception) { } // We should have gotten an error from CancelRequest(). Whether we did or not, what we // really have is a timeout exception, and that will be less confusing to the user than // "operation cancelled by user" or similar, so whatever the case, that is what we'll throw. // Changed message again to report about the two possible timeouts: connection or command // as the establishment timeout only was confusing users when the timeout was a command timeout. } throw new NpgsqlException(resman.GetString("Exception_ConnectionOrCommandTimeout")); } switch (context.BackendProtocolVersion) { case ProtocolVersion.Version2: return ProcessBackendResponses_Ver_2(context); case ProtocolVersion.Version3: return ProcessBackendResponses_Ver_3(context); default: throw new NpgsqlException(resman.GetString("Exception_UnknownProtocol")); } } catch(ThreadAbortException) { try { context.CancelRequest(); context.Close(); } catch {} throw; } }
/// <summary> /// Creates another connector and sends a cancel request through it for this connector. /// </summary> internal void CancelRequest() { var cancelConnector = new NpgsqlConnector(_settings, false); try { cancelConnector.RawOpen(cancelConnector.ConnectionTimeout*1000); cancelConnector.SendSingleMessage(new CancelRequestMessage(BackendProcessId, BackendSecretKey)); } finally { cancelConnector.Close(); } }
/// <summary> /// Find an available pooled connector in the non-shared pool, or create /// a new one if none found. /// </summary> private NpgsqlConnector GetPooledConnector(NpgsqlConnection Connection) { ConnectorQueue Queue; NpgsqlConnector Connector = null; // We only need to lock all pools when trying to get one pool or create one. lock (locker) { // Try to find a queue. if (!PooledConnectors.TryGetValue(Connection.ConnectionString, out Queue)) { Queue = new ConnectorQueue(); Queue.ConnectionLifeTime = Connection.ConnectionLifeTime; Queue.MinPoolSize = Connection.MinPoolSize; PooledConnectors[Connection.ConnectionString] = Queue; } } // Now we can simply lock on the pool itself. lock (Queue) { if (Queue.Available.Count > 0) { // Found a queue with connectors. Grab the top one. // Check if the connector is still valid. Connector = Queue.Available.Dequeue(); Queue.Busy.Add(Connector, null); } } if (Connector != null) { if (!Connector.IsValid()) { lock (Queue) { Queue.Busy.Remove(Connector); } Connector.Close(); return(GetPooledConnector(Connection)); //Try again } return(Connector); } lock (Queue) { if (Queue.Available.Count + Queue.Busy.Count < Connection.MaxPoolSize) { Connector = new NpgsqlConnector(Connection); Queue.Busy.Add(Connector, null); } } if (Connector != null) { Connector.ProvideClientCertificatesCallback += Connection.ProvideClientCertificatesCallbackDelegate; Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; Connector.ValidateRemoteCertificateCallback += Connection.ValidateRemoteCertificateCallbackDelegate; try { Connector.Open(); } catch { lock (Queue) { Queue.Busy.Remove(Connector); } Connector.Close(); throw; } // Meet the MinPoolSize requirement if needed. if (Connection.MinPoolSize > 1) { lock (Queue) { while (Queue.Available.Count + Queue.Busy.Count < Connection.MinPoolSize) { NpgsqlConnector Spare = new NpgsqlConnector(Connection); Spare.ProvideClientCertificatesCallback += Connection.ProvideClientCertificatesCallbackDelegate; Spare.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; Spare.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; Spare.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; Spare.ValidateRemoteCertificateCallback += Connection.ValidateRemoteCertificateCallbackDelegate; Spare.Open(); Spare.ProvideClientCertificatesCallback -= Connection.ProvideClientCertificatesCallbackDelegate; Spare.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Spare.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Spare.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; Spare.ValidateRemoteCertificateCallback -= Connection.ValidateRemoteCertificateCallbackDelegate; Queue.Available.Enqueue(Spare); } } } } return(Connector); }
/// <summary> /// Put a pooled connector into the pool queue. /// </summary> /// <param name="Connector">Connector to pool</param> private void UngetPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) { ConnectorQueue Queue; // Find the queue. Queue = (ConnectorQueue)PooledConnectors[Connector.ConnectionString.ToString()]; if (Queue == null) { throw new InvalidOperationException("Internal: No connector queue found for existing connector."); } Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; Queue.UseCount--; if (! Connector.IsInitialized) { if (Connector.Transaction != null) { Connector.Transaction.Cancel(); } Connector.Close(); } else { if (Connector.Transaction != null) { try { Connector.Transaction.Rollback(); } catch { Connector.Close() ; } } } if (Connector.State == System.Data.ConnectionState.Open) { // Release all plans and portals associated with this connector. Connector.ReleasePlansPortals(); Queue.Enqueue(Connector); } }
///<summary> /// This method is responsible to handle all protocol messages sent from the backend. /// It holds all the logic to do it. /// To exchange data, it uses a Mediator object from which it reads/writes information /// to handle backend requests. /// </summary> /// internal IEnumerable<IServerResponseObject> ProcessBackendResponsesEnum(NpgsqlConnector context) { try { // Flush buffers to the wire. context.Stream.Flush(); // Process commandTimeout behavior. if ((context.Mediator.BackendCommandTimeout > 0) && (!CheckForContextSocketAvailability(context, SelectMode.SelectRead))) { // If timeout occurs when establishing the session with server then // throw an exception instead of trying to cancel query. This helps to prevent loop as // CancelRequest will also try to stablish a connection and sends commands. if (!((this is NpgsqlStartupState || this is NpgsqlConnectedState))) { try { context.CancelRequest(); ProcessAndDiscardBackendResponses(context); } catch(Exception) { } // We should have gotten an error from CancelRequest(). Whether we did or not, what we // really have is a timeout exception, and that will be less confusing to the user than // "operation cancelled by user" or similar, so whatever the case, that is what we'll throw. // Changed message again to report about the two possible timeouts: connection or command // as the establishment timeout only was confusing users when the timeout was a command timeout. } throw new NpgsqlException(resman.GetString("Exception_ConnectionOrCommandTimeout")); } return ProcessBackendResponses(context); } catch(ThreadAbortException) { try { context.CancelRequest(); context.Close(); } catch {} throw; } }