/// <summary> /// Attempts to ensure, on a best-effort basis, that there are enough connections to meet MinPoolSize. /// This method never throws an exception. /// </summary> void EnsureMinPoolSize(NpgsqlConnection conn) { int missing; lock (this) { missing = _min - (Busy + Idle.Count); if (missing <= 0) { return; } Busy += missing; } for (; missing > 0; missing--) { try { var connector = new NpgsqlConnector((NpgsqlConnection)((ICloneable)conn).Clone()) { ClearCounter = _clearCounter }; // TODO: Think about the timeout here... connector.Open(new NpgsqlTimeout(TimeSpan.Zero), false, CancellationToken.None).Wait(); connector.Reset(); Counters.NumberOfPooledConnections.Increment(); lock (this) { Idle.Push(connector); EnsurePruningTimerState(); Busy--; } } catch (Exception e) { lock (this) Busy -= missing; Log.Warn("Connection error while attempting to ensure MinPoolSize", e); return; } } }
internal async Task <NpgsqlConnector> AllocateLong(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) { Debug.Assert(Monitor.IsEntered(this)); NpgsqlConnector connector; Debug.Assert(Busy <= _max); if (Busy == _max) { // TODO: Async cancellation var tcs = new TaskCompletionSource <NpgsqlConnector>(); _waiting.Enqueue(new WaitingOpenAttempt { TaskCompletionSource = tcs, IsAsync = async }); Monitor.Exit(this); try { if (async) { if (timeout.IsSet) { var timeLeft = timeout.TimeLeft; if (timeLeft <= TimeSpan.Zero || tcs.Task != await Task.WhenAny(tcs.Task, Task.Delay(timeLeft))) { throw new NpgsqlException($"The connection pool has been exhausted, either raise MaxPoolSize (currently {_max}) or Timeout (currently {Settings.Timeout} seconds)"); } } else { await tcs.Task; } } else { if (timeout.IsSet) { var timeLeft = timeout.TimeLeft; if (timeLeft <= TimeSpan.Zero || !tcs.Task.Wait(timeLeft)) { throw new NpgsqlException($"The connection pool has been exhausted, either raise MaxPoolSize (currently {_max}) or Timeout (currently {Settings.Timeout} seconds)"); } } else { tcs.Task.Wait(); } } } catch { // We're here if the timeout expired or the cancellation token was triggered // Re-lock and check in case the task was set to completed after coming out of the Wait lock (this) { if (!tcs.Task.IsCompleted) { tcs.SetCanceled(); throw; } } } connector = tcs.Task.Result; connector.Connection = conn; return(connector); } // No idle connectors are available, and we're under the pool's maximum capacity. IncrementBusy(); Monitor.Exit(this); try { connector = new NpgsqlConnector(conn) { ClearCounter = _clearCounter }; await connector.Open(timeout, async, cancellationToken); Counters.NumberOfPooledConnections.Increment(); EnsureMinPoolSize(conn); return(connector); } catch { lock (this) DecrementBusy(); throw; } }