1 // Copyright (C) 2002 The Npgsql Development Team
2 // npgsql-general@gborg.postgresql.org
3 // http://gborg.postgresql.org/project/npgsql/projdisplay.php
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // ------------------------------------------------------------------
22 // 0.00.0000 - 06/17/2002 - ulrich sprick - creation
23 // - 05/??/2004 - Glen Parker<glenebob@nwlink.com> rewritten using
27 using System.Collections;
28 using System.Threading;
33 /// This class manages all connector objects, pooled AND non-pooled.
35 internal class NpgsqlConnectorPool
38 /// A queue with an extra Int32 for keeping track of busy connections.
40 private class ConnectorQueue : System.Collections.Queue
43 /// The number of pooled Connectors that belong to this queue but
44 /// are currently in use.
46 public Int32 UseCount = 0;
49 /// <value>Unique static instance of the connector pool
51 internal static NpgsqlConnectorPool ConnectorPoolMgr = new NpgsqlConnectorPool();
53 public NpgsqlConnectorPool()
55 PooledConnectors = new Hashtable();
59 /// <value>Map of index to unused pooled connectors, avaliable to the
60 /// next RequestConnector() call.</value>
61 /// <remarks>This hashmap will be indexed by connection string.
62 /// This key will hold a list of queues of pooled connectors available to be used.</remarks>
63 private Hashtable PooledConnectors;
65 /// <value>Map of shared connectors, avaliable to the
66 /// next RequestConnector() call.</value>
67 /// <remarks>This hashmap will be indexed by connection string.
68 /// This key will hold a list of shared connectors available to be used.</remarks>
70 //private Hashtable SharedConnectors;
73 /// Searches the shared and pooled connector lists for a
74 /// matching connector object or creates a new one.
76 /// <param name="Connection">The NpgsqlConnection that is requesting
77 /// the connector. Its ConnectionString will be used to search the
78 /// pool for available connectors.</param>
79 /// <returns>A connector object.</returns>
80 public NpgsqlConnector RequestConnector (NpgsqlConnection Connection)
82 NpgsqlConnector Connector;
84 if (Connection.Pooling)
86 Connector = RequestPooledConnector(Connection);
90 Connector = GetNonPooledConnector(Connection);
97 /// Find a pooled connector. Handle locking and timeout here.
99 private NpgsqlConnector RequestPooledConnector (NpgsqlConnection Connection)
101 NpgsqlConnector Connector;
102 Int32 timeoutMilliseconds = Connection.Timeout * 1000;
106 Connector = RequestPooledConnectorInternal(Connection);
109 while (Connector == null && timeoutMilliseconds > 0)
111 Int32 ST = timeoutMilliseconds > 1000 ? 1000 : timeoutMilliseconds;
114 timeoutMilliseconds -= ST;
118 Connector = RequestPooledConnectorInternal(Connection);
122 if (Connector == null)
124 if (Connection.Timeout > 0)
126 throw new Exception("Timeout while getting a connection from pool.");
130 throw new Exception("Connection pool exceeds maximum size.");
138 /// Find a pooled connector. Handle shared/non-shared here.
140 private NpgsqlConnector RequestPooledConnectorInternal (NpgsqlConnection Connection)
142 NpgsqlConnector Connector = null;
143 Boolean Shared = false;
145 // If sharing were implemented, I suppose Shared would be set based
146 // on some property on the Connection.
150 Connector = GetPooledConnector(Connection);
154 // Connection sharing? What's that?
155 throw new NotImplementedException("Internal: Shared pooling not implemented");
162 /// Releases a connector, possibly back to the pool for future use.
165 /// Pooled connectors will be put back into the pool if there is room.
166 /// Shared connectors should just have their use count decremented
167 /// since they always stay in the shared pool.
169 /// <param name="Connector">The connector to release.</param>
170 /// <param name="ForceClose">Force the connector to close, even if it is pooled.</param>
171 public void ReleaseConnector (NpgsqlConnection Connection, NpgsqlConnector Connector)
173 if (Connector.Pooled)
175 ReleasePooledConnector(Connection, Connector);
179 UngetNonPooledConnector(Connection, Connector);
184 /// Release a pooled connector. Handle locking here.
186 private void ReleasePooledConnector (NpgsqlConnection Connection, NpgsqlConnector Connector)
190 ReleasePooledConnectorInternal(Connection, Connector);
195 /// Release a pooled connector. Handle shared/non-shared here.
197 private void ReleasePooledConnectorInternal (NpgsqlConnection Connection, NpgsqlConnector Connector)
199 if (! Connector.Shared)
201 UngetPooledConnector(Connection, Connector);
205 // Connection sharing? What's that?
206 throw new NotImplementedException("Internal: Shared pooling not implemented");
211 /// Create a connector without any pooling functionality.
213 private NpgsqlConnector GetNonPooledConnector(NpgsqlConnection Connection)
215 NpgsqlConnector Connector;
217 Connector = CreateConnector(Connection);
219 Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate;
220 Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate;
221 Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate;
229 /// Find an available pooled connector in the non-shared pool, or create
230 /// a new one if none found.
232 private NpgsqlConnector GetPooledConnector(NpgsqlConnection Connection)
234 ConnectorQueue Queue;
235 NpgsqlConnector Connector = null;
237 // Try to find a queue.
238 Queue = (ConnectorQueue)PooledConnectors[Connection.ConnectionString.ToString()];
242 Queue = new ConnectorQueue();
243 PooledConnectors[Connection.ConnectionString.ToString()] = Queue;
248 // Found a queue with connectors. Grab the top one.
250 // Check if the connector is still valid.
254 Connector = (NpgsqlConnector)Queue.Dequeue();
255 if (Connector.IsValid())
263 if (Queue.Count <= 0)
264 return GetPooledConnector(Connection);
272 else if (Queue.Count + Queue.UseCount < Connection.MaxPoolSize)
274 Connector = CreateConnector(Connection);
276 Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate;
277 Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate;
278 Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate;
298 // Meet the MinPoolSize requirement if needed.
299 if (Connection.MinPoolSize > 0)
301 while (Queue.Count + Queue.UseCount < Connection.MinPoolSize)
303 NpgsqlConnector Spare = CreateConnector(Connection);
305 Spare.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate;
306 Spare.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate;
307 Spare.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate;
311 Spare.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate;
312 Spare.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate;
313 Spare.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate;
315 Queue.Enqueue(Connector);
323 /// Find an available shared connector in the shared pool, or create
324 /// a new one if none found.
326 private NpgsqlConnector GetSharedConnector(NpgsqlConnection Connection)
333 private NpgsqlConnector CreateConnector(NpgsqlConnection Connection)
335 return new NpgsqlConnector(
336 Connection.ConnectionStringValues.Clone(),
344 /// This method is only called when NpgsqlConnection.Dispose(false) is called which means a
345 /// finalization. This also means, an NpgsqlConnection was leak. We clear pool count so that
346 /// client doesn't end running out of connections from pool. When the connection is finalized, its underlying
347 /// socket is closed.
349 public void FixPoolCountBecauseOfConnectionDisposeFalse(NpgsqlConnection Connection)
351 ConnectorQueue Queue;
353 // Prevent multithread access to connection pool count.
356 // Try to find a queue.
357 Queue = (ConnectorQueue)PooledConnectors[Connection.ConnectionString.ToString()];
366 /// Close the connector.
368 /// <param name="Connector">Connector to release</param>
369 private void UngetNonPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector)
371 Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate;
372 Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate;
373 Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate;
375 if (Connector.Transaction != null)
377 Connector.Transaction.Cancel();
384 /// Put a pooled connector into the pool queue.
386 /// <param name="Connector">Connector to pool</param>
387 private void UngetPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector)
389 ConnectorQueue Queue;
392 Queue = (ConnectorQueue)PooledConnectors[Connector.ConnectionString.ToString()];
396 throw new InvalidOperationException("Internal: No connector queue found for existing connector.");
399 Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate;
400 Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate;
401 Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate;
405 if (! Connector.IsInitialized)
407 if (Connector.Transaction != null)
409 Connector.Transaction.Cancel();
416 if (Connector.Transaction != null)
420 Connector.Transaction.Rollback();
429 if (Connector.State == System.Data.ConnectionState.Open)
431 // Release all plans and portals associated with this connector.
432 Connector.ReleasePlansPortals();
434 Queue.Enqueue(Connector);
439 /// Stop sharing a shared connector.
441 /// <param name="Connector">Connector to unshare</param>
442 private void UngetSharedConnector(NpgsqlConnection Connection, NpgsqlConnector Connector)