Hi again,
I've tried the rebuild with the modified file, using NANT, but the
problem is still there. The connections just keep growing until I get
the timeout error.
I've also noticed that when I disable Pooling, after 10 minutes of
heavy use, the Web app will slow down to about 50 seconds to refresh a
page then stop with error "Cannot complete connection to 'localhost'".
It must be something I'm not setting properly?
Cheers
Eric
sasha wrote:
There were
a few bugs in Provider in connection pooling. We fixed them and sent
bugreports to Carlos. But i don't know if he commit fixes in 1.7.1 or
not.
You can rebuild provider yourself with our fixes. Replace original
FBConnectionPool.cs file from attachment.
--
This email has been verified as Virus free
Virus Protection and more available at http://www.plus.net
/*
* Firebird ADO.NET Data provider for .NET and Mono
*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.firebirdsql.org/index.php?op=doc&id=idpl
*
* Software distributed under the License is distributed on
* an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the License for the specific
* language governing rights and limitations under the License.
*
* Copyright (c) 2002, 2005 Carlos Guzman Alvarez
* All Rights Reserved.
*/
using System;
using System.Data;
using System.Collections;
using System.Threading;
using FirebirdSql.Data.Common;
namespace FirebirdSql.Data.Firebird
{
internal sealed class FbPoolManager
{
#region Static fields
public static readonly FbPoolManager Instance = new FbPoolManager();
#endregion
#region Fields
private Hashtable pools;
private Hashtable handlers;
private object syncObject;
#endregion
#region Properties
public int PoolsCount
{
get
{
if (this.pools != null)
{
return this.pools.Count;
}
return 0;
}
}
#endregion
#region Constructors
private FbPoolManager()
{
this.pools = Hashtable.Synchronized(new Hashtable());
this.handlers = Hashtable.Synchronized(new Hashtable());
this.syncObject = new object();
}
#endregion
#region Methods
public FbConnectionPool FindPool(string connectionString)
{
FbConnectionPool pool = null;
lock (this.syncObject)
{
if (this.pools.ContainsKey(connectionString.GetHashCode()))
{
pool = (FbConnectionPool)pools[connectionString.GetHashCode()];
}
}
return pool;
}
public FbConnectionPool CreatePool(string connectionString)
{
FbConnectionPool pool = null;
lock (this.syncObject)
{
pool = this.FindPool(connectionString);
if (pool == null)
{
lock (this.pools.SyncRoot)
{
int hashcode = connectionString.GetHashCode();
// Create an empty pool handler
EmptyPoolEventHandler handler = new EmptyPoolEventHandler(this.OnEmptyPool);
this.handlers.Add(hashcode, handler);
// Create the new connection pool
pool = new FbConnectionPool(connectionString);
this.pools.Add(hashcode, pool);
pool.EmptyPool += handler;
}
}
}
return pool;
}
public void ClearAllPools()
{
lock (this.syncObject)
{
lock (this.pools.SyncRoot)
{
FbConnectionPool[] tempPools = new FbConnectionPool[this.pools.Count];
this.pools.Values.CopyTo(tempPools, 0);
foreach (FbConnectionPool pool in tempPools)
{
// Clear pool
pool.Clear();
}
// Clear Hashtables
this.pools.Clear();
this.handlers.Clear();
}
}
}
public void ClearPool(string connectionString)
{
lock (this.syncObject)
{
lock (this.pools.SyncRoot)
{
int hashCode = connectionString.GetHashCode();
if (this.pools.ContainsKey(hashCode))
{
FbConnectionPool pool = (FbConnectionPool)this.pools[hashCode];
// Clear pool
pool.Clear();
}
}
}
}
#endregion
#region Private Methods
private void OnEmptyPool(object sender, EventArgs e)
{
lock (this.pools.SyncRoot)
{
int hashCode = (int)sender;
if (this.pools.ContainsKey(hashCode))
{
FbConnectionPool pool = (FbConnectionPool)this.pools[hashCode];
EmptyPoolEventHandler handler = (EmptyPoolEventHandler)this.handlers[hashCode];
pool.EmptyPool -= handler;
this.pools.Remove(hashCode);
this.handlers.Remove(hashCode);
pool = null;
handler = null;
}
}
}
#endregion
}
internal delegate void EmptyPoolEventHandler(object sender, EventArgs e);
internal class FbConnectionPool : MarshalByRefObject
{
#region Types
private enum MoveType {LockedToUnlocked, UnlockedToLocked}
#endregion
#region Fields
private FbConnectionString options;
private ArrayList locked;
private ArrayList unlocked;
private Thread cleanUpThread;
private string connectionString;
private bool isRunning;
private long lifeTime;
private object syncObject;
#endregion
#region Events
public event EmptyPoolEventHandler EmptyPool;
#endregion
#region Properties
public int Count
{
get
{
lock (this.unlocked.SyncRoot)
{
return this.unlocked.Count + this.locked.Count;
}
}
}
public bool HasUnlocked
{
get { return this.unlocked.Count > 0; }
}
#endregion
#region Constructors
public FbConnectionPool(string connectionString)
{
this.syncObject = new object();
this.connectionString = connectionString;
this.options = new FbConnectionString(connectionString);
this.lifeTime = this.options.ConnectionLifeTime * TimeSpan.TicksPerSecond;
if (this.options.MaxPoolSize == 0)
{
this.locked = ArrayList.Synchronized(new ArrayList());
this.unlocked = ArrayList.Synchronized(new ArrayList());
}
else
{
this.locked = ArrayList.Synchronized(new ArrayList(this.options.MaxPoolSize));
this.unlocked = ArrayList.Synchronized(new ArrayList(this.options.MaxPoolSize));
}
// If a minimun number of connections is requested
// initialize the pool
this.Initialize();
// Start the cleanup thread only if needed
if (this.lifeTime != 0)
{
this.isRunning = true;
this.cleanUpThread = new Thread(new ThreadStart(this.RunCleanup));
this.cleanUpThread.Name = "Cleanup Thread";
this.cleanUpThread.Start();
this.cleanUpThread.IsBackground = true;
}
}
#endregion
#region Methods
private void MoveConnection(FbConnectionInternal connection, MoveType moveType)
{
if (null == connection)
return;
lock (this.unlocked.SyncRoot)
{
switch (moveType)
{
case MoveType.LockedToUnlocked:
this.locked.Remove(connection);
this.unlocked.Add(connection);
break;
case MoveType.UnlockedToLocked:
this.unlocked.Remove(connection);
this.locked.Add(connection);
break;
}
}
}
public void CheckIn(FbConnectionInternal connection)
{
connection.OwningConnection = null;
connection.Created = System.DateTime.Now.Ticks;
MoveConnection(connection, MoveType.LockedToUnlocked);
}
public FbConnectionInternal CheckOut()
{
FbConnectionInternal newConnection = null;
lock (this.syncObject)
{
// 1. Try to get a connection from the unlocked connection list
newConnection = this.GetConnection();
if (newConnection != null)
return newConnection;
// 2. Check if we have reached the max number of allowed connections
this.CheckMaxPoolSize();
// 3. Try to get a connection from the unlocked connection list
newConnection = this.GetConnection();
if (newConnection != null)
return newConnection;
// 4. In any other case create a new connection
newConnection = this.Create();
// Set connection pooling settings to the new connection
newConnection.Lifetime = this.options.ConnectionLifeTime;
newConnection.Pooled = true;
// Added to the locked connections list.
this.locked.Add(newConnection);
}
return newConnection;
}
public void Clear()
{
lock (this.syncObject)
{
// Stop cleanup thread
if (this.cleanUpThread != null)
{
this.cleanUpThread.Abort();
this.cleanUpThread.Join();
}
// Close all unlocked connections
FbConnectionInternal[] list = (FbConnectionInternal[]) this.unlocked.ToArray(typeof(FbConnectionInternal));
foreach (FbConnectionInternal connection in list)
{
connection.Disconnect();
}
// Close all locked connections
list = (FbConnectionInternal[])this.locked.ToArray(typeof(FbConnectionInternal));
foreach (FbConnectionInternal connection in list)
{
connection.Disconnect();
}
// Clear lists
this.unlocked.Clear();
this.locked.Clear();
// Raise EmptyPool event
if (this.EmptyPool != null)
{
this.EmptyPool(this.connectionString.GetHashCode(), null);
}
// Reset fields
this.unlocked = null;
this.locked = null;
this.connectionString = null;
this.cleanUpThread = null;
this.EmptyPool = null;
}
}
#endregion
#region Private Methods
private bool CheckMinPoolSize()
{
if (this.options.MinPoolSize > 0 && this.Count <= this.options.MinPoolSize)
{
return false;
}
else
{
return true;
}
}
private void CheckMaxPoolSize()
{
if (this.options.MaxPoolSize > 0 && this.Count >= this.options.MaxPoolSize)
{
long timeout = this.options.ConnectionTimeout * TimeSpan.TicksPerSecond;
long start = DateTime.Now.Ticks;
/*
Loop brakes without errors in next situations:
1. connection was returned from locked to unlocked by calling CheckIn in other thread (HasUnlocked = true)
2. connection was moved from locked to unlocked (by Checkin) and then cleaned (removed from unlocked by Cleanup)
*/
while (true)
{
if (this.Count >= this.options.MaxPoolSize && this.HasUnlocked == false)
{
if ((DateTime.Now.Ticks - start) > timeout)
throw new SystemException("Timeout exceeded.");
Thread.Sleep(100);
}
else
break;
}
}
}
private void Initialize()
{
lock (this.syncObject)
{
for (int i = 0; i < this.options.MinPoolSize; i++)
{
this.unlocked.Add(this.Create());
}
}
}
private FbConnectionInternal Create()
{
FbConnectionInternal connection = new FbConnectionInternal(this.options);
connection.Connect();
connection.Pooled = true;
connection.Created = DateTime.Now.Ticks;
return connection;
}
private FbConnectionInternal GetConnection()
{
FbConnectionInternal result = null;
long check = -1;
lock (this.unlocked.SyncRoot)
{
FbConnectionInternal[] connections = (FbConnectionInternal[]) this.unlocked.ToArray(typeof(FbConnectionInternal));
for (int i = connections.Length - 1; i >= 0; i--)
{
if (connections[i].Verify())
{
if (this.lifeTime != 0)
{
long now = DateTime.Now.Ticks;
long expire = connections[i].Created + this.lifeTime;
if (now >= expire)
{
if (this.CheckMinPoolSize())
{
this.unlocked.Remove(connections[i]);
this.Expire(connections[i]);
}
}
else
{
if (expire > check)
{
check = expire;
result = connections[i];
}
}
}
else
{
result = connections[i];
break;
}
}
else
{
this.unlocked.Remove(connections[i]);
this.Expire(connections[i]);
}
}
if (result != null)
MoveConnection(result, MoveType.UnlockedToLocked);
}
return result;
}
private void RunCleanup()
{
int interval = Convert.ToInt32(TimeSpan.FromTicks(this.lifeTime).TotalMilliseconds);
if (interval > 60000)
{
interval = 60000;
}
try
{
while (this.isRunning)
{
Thread.Sleep(interval);
this.Cleanup();
if (this.Count == 0)
{
lock (this.syncObject)
{
// Empty pool
if (this.EmptyPool != null)
{
this.EmptyPool(this.connectionString.GetHashCode(), null);
}
// Stop running
this.isRunning = false;
}
}
}
}
catch (ThreadAbortException)
{
this.isRunning = false;
}
}
private void Expire(FbConnectionInternal connection)
{
try
{
if (connection.Verify())
{
connection.Disconnect();
}
}
catch (Exception)
{
throw new FbException("Error closing database connection.");
}
}
private void Cleanup()
{
lock (this.unlocked.SyncRoot)
{
if (this.unlocked.Count > 0 && this.lifeTime != 0)
{
FbConnectionInternal[] list = (FbConnectionInternal[])this.unlocked.ToArray(typeof(FbConnectionInternal));
foreach (FbConnectionInternal connection in list)
{
long now = DateTime.Now.Ticks;
long expire = connection.Created + this.lifeTime;
if (now >= expire)
{
if (this.CheckMinPoolSize())
{
this.unlocked.Remove(connection);
this.Expire(connection);
}
}
}
}
}
}
#endregion
}
}
No virus found in this incoming message.
Checked by AVG Free Edition.
Version: 7.1.385 / Virus Database: 268.3.5/300 - Release Date: 03/04/2006
|
- Re: [Firebird-net-provider] Re: Connection Pooling... Eric Walmsley
-