Connection Pooling not working  
Author Message
JohnGalt





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

I have a form that has a number of database calls. It does these sequentially. In some cases it's not possible (due to archetecture more than anything) to make these calls all on the same connection. However it does open then close the connection. (all connections created are in using statements)

What I'm seeing is that we're getting a timeout when the 100 connection limit is hit. Looking at Activity Monitor I can see these connections growing and growing over time in the activity monitor the more windows you open and close even though these connections are closed. (and they all use exactly the same connection string)

data source=(Local);initial catalog=Evolution;password=dyknalna;user id=EvolutionUser;Connection Timeout=20;

I read that it might be because of SQL Debugging being turned on in vs.net, but that is not enabled in either debug or runtime for us and we're seeing it in client sites as well as locally.

We're running .NET 2.0.

Does anyone have any ideas as to why it won't reuse these clearly properly closed connections (it appears after some time, the connections time out and are released, which is why our app isn't constantly crashing, but if you're working through data intensive actions like creating lots of invoices etc. It will run out of connections.

Thanks!

(sorry for the double post in another forum, I got the wrong one!)



.NET Development3  
 
 
VMazur





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

I believe connections are not closed in your application. You could check it executing sp_who in a SQL Server Query Analyzer or Management console for the SQL Server. Try to call Close and Dispose methods of the connection explicitly in your code

 
 
JohnGalt





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

Every single connection is in a using() {} structure so by defintion it has been closed....
 
 
William Vaughn





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

First, let me make sure I understand your architecture. You say "forms" so I'll assume that this is a Windows Forms-based application and not ASP. In this case Connection pooling might not help at all--as a matter of fact it probably slows you down. Remember that a Connection pool is created for each process (a new one for each unique ConnectionString and Transaction within the process). This means that it's possible (and more performant) to open a connection and use it for the life of the application--closing it on termination.

However, if you've already written your app you don't want to reengineer it so let's focus on your problem. There are many reasons for the pool to overflow (as it has) at 100 connections. The two most common are:

  1. Connections not being closed. As I discuss in my HHG 7th Ed. Chapter 9 (at length) the most common of these is passing a "live" DataReader (one that's feeding rows from an open Connection) to another scope. In this case, it becomes the responsibility of the external scope to close the Connection. Since we often create Connection objects in the scope of the DataReader, it's not visible outside that scope thus the "receiving" scope can't close it. What it can do is close the DataReader and that's easy. Either use the Close or Load method or pass it to a complex bound control. But that won't close the Connection unless you have set CommandBehavior.CloseConnection on the DataReader. In this case you'll leak a Connection on each operation that calls the out-of-scope routine.  Another culprit here is exception handling. In this case, if the process of fetching rows from the DataReader throws an exception, this might short-circuit the code to close the DataReader or the Connection (or both). Typically, this results in a much slower leak.
  2. Connections being overloaded. In an ASP application or (more rarely) in a multi-threaded Windows Forms application, you can overload the server--the amount of work that that needs to be done exceeds the ability of the server to do it on the first connection. In this case the Connection Pooling mechanism must open another connection to the server to service the next request instead of using an idle connection in the pool. Frankly, I have not seen this situation reported in a Windows Forms application--it's very common in ASP.

I would never leave the job of closing connections to a destructor (Dispose)--but that's just me. I make sure to add the code to explicitily Close the Connections in the Using block.

Yes, it's entirely reasonable to open more than one Connection in a Windows forms application because a Connection can only process one operation at a time (in SQL Server)--unless you use MARS--and no, I don't endorse MARS as it causes more problems than it solves IMHO. That said, the number of connections generally does not exceed one per thread--usually 2-5 at most. I would get the Performance Monitor fired up and start stepping through your application to see if Connections are being closed. There are a host of SQL Server Performance Counters that you can poll to see where the Connections are being leaked. It's too bad you can't just put dye in the coolant to see where it's coming out of the block like you can with a car engine... ;)



 
 
kbradl1





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

Can you post the code If you are using a DataReader you need to close the datareader after using it. Closing the connection is not enough.

 
 
JohnGalt





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

They ALL go like this:

using (System.Data.IDBConnection con = new Connection(<We do this from a class>)) {

using (System.Data.IDBCommand cmd = con.CreateCommand(CommandBehavior.CloseConnection)) {

cmd.CommandText = ....;

using(System.Data.IDataReader dr = cmd.ExecuteReader()) {

...Do stuff.

}

}

}

Thus there is NO CHANCE of the connection being left open. It will either be closed automatically by the datareader or closed automatically by the Using.

The only trick is that it does this over different WinForms which is the only thing I can see going on. If it's on one form, it does it's thing correctly, if it's on multiple forms it doesn't.


 
 
Damiaan





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

The using statement only calls the dispose of the object, it doesn't close it i think.

// using definition
using ( type variable = initialization ) embeddedStatement
// ... translates to:
{
type variable = initialization;
try
{
embeddedStatement
}
finally
{
if (variable != null)
{
((IDisposable)variable).Dispose();
}
}
}
For Connection Pooling to work, you MUST close connections. Unclosed
connections will not be pooled.
Try calling the close statement explicitly.

Edit:
The SqlConnection class also has a method called Dispose, which also closes the connection, and performs any other needed clean-up.
As you are using an interface instead of the SqlConnection class you should take care that you close the connection yourself.



 
 
JohnGalt





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

As per the documentation, Dispose ALWAYS calls close first on all connections and streams.

IDBConnection as an interface impliments Dispose and calls the close method which is on the interface. This was by design so that you can always return an IDBConnection and it will respond the same way that an actual connection would. (i.e. it's drop in compatible)

This is in the docs that talks about iDBConnection in msdn help.

And as I said, it works great... but not on multple forms.


 
 
Matt Neerincx





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

In general if you hit the limit for the pool, something is keeping the connections opened. It could be pending distributed transactions as well, double check this.

One trick I did to track this one down is capture stack trace for each connection opened and store in hash table, then remove from hash table right when connection is disposed. Then you can dump the connections and stack traces when you hit the limit to see what functions are causing the problem. I used this with ASP.NET code where it has to be thread safe, etc... so it should work anywhere.

If you don't want to go down this route, a simpler method would be to dump out stack trace and SPID of connection right after connection is opened. Then when the problem occurs run "select * from master..sysprocesses" to get list of opened SPIDs and use this with your stack trace log to see what stack traces are leaving opened connections.

Note I'm not sure if this code works with .NET 2.0 however!

using System;
using System.Collections;
using System.Reflection;
using System.Text;

namespace ConnTrack
{
public class ConnTrack
{
// Initial stack frames to skip when walking stack.
public const int STACK_SKIP_FRAMES = 1;

// Maximum stack depth to capture.
public const int MAX_STACK_DEPTH = 20;

// This is just handy constant to save or-ing all these BindingFlags values over and over in my code.
public const BindingFlags BINDING_FLAGS = (BindingFlags.DeclaredOnly|BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.GetField);

// Main internal cconnection hash table.
public static Hashtable connectionHash = new Hashtable();

// Current maximum stack string size (dynamically grows to accomodate largest stack).
public int maxStackStringSize = 1000;

// Failure counters so we can examine if class is having problems.
public int failedNullConnection = 0;
public int failedNullInternalConnection = 0;
public int failedStackWalk = 0;
public int failedSaveToHashConnection = 0;
public int failedTrackOpenConnection = 0;
public int failedTrackDisposeConnection = 0;

// Success counters to track open and closes.
public int successTrackOpenConnection = 0;
public int successTrackDisposeConnection = 0;

// External callers call this function after opening a connection.
// Function captures stack trace and stores this in the connectionHash hash table
// using the hashcode of the _internalConnection member variable of the connection.
// Works with both SqlConnection and OracleConnection objects.
public void TrackOpenConnection( object conn )
{
int i, maxStackDepth, icHashCode;
System.Diagnostics.StackTrace stack;
System.Diagnostics.StackFrame frame;
StringBuilder sb;

// Check for null input, track errors.
if ( null == conn )
{
failedNullConnection++;
failedTrackOpenConnection++;
return;
}

// Capture stack trace for connection.
try
{
// Get connection's internalConnection hash code.
// This will return 0 on failure.
icHashCode = GetInternalConnectionHashCode( conn );
if ( 0 == icHashCode )
{
failedTrackOpenConnection++;
return;
}

stack = new System.Diagnostics.StackTrace( STACK_SKIP_FRAMES, true );
sb = new StringBuilder( 1000 );
sb.Append( "c.hc=" );
sb.Append( conn.GetHashCode().ToString() );
sb.Append( "ic.hc=" );
sb.Append( icHashCode.ToString() );
sb.Append( " ticks=" );
sb.Append( System.Environment.TickCount.ToString() );
sb.Append( " tid=" );
sb.Append( System.AppDomain.GetCurrentThreadId().ToString() );
sb.Append( "<br>" );
maxStackDepth = ( stack.FrameCount > MAX_STACK_DEPTH ) MAX_STACK_DEPTH : stack.FrameCount;
for ( i=0; i<maxStackDepth; i++ )
{
frame = stack.GetFrame(i);
if ( null != frame )
{
sb.Append( '[' );
sb.Append( i.ToString() );
sb.Append( "]{" );
sb.Append( frame.GetMethod().Name );
sb.Append( '|' );
sb.Append( frame.GetFileName() );
sb.Append( '|' );
sb.Append( frame.GetFileLineNumber().ToString() );
sb.Append( '}' );
sb.Append( "<br>" );
}
}
}
catch ( Exception ex )
{
string s = ex.Message;
failedStackWalk++;
failedTrackOpenConnection++;
return;
}

// Ok if we get this far, save off stack info to hash table.
lock( connectionHash )
{
try
{
// Add stack to hash table.
connectionHash[icHashCode] = sb.ToString();

// Bump up string builder size to accomodate worst case stack.
// This will also server to grainularize allocations and improve performance.
if ( sb.Length > maxStackStringSize ) maxStackStringSize = sb.Length;

// Track successes.
successTrackOpenConnection++;
}
catch ( Exception )
{
failedSaveToHashConnection++;
failedTrackOpenConnection++;
}
}
}
// Caller calls this function prior to Closing/Disposing connection to remove the connection
// from the stack tracking hash table.
public void TrackDisposeConnection( object conn )
{
int icHashCode;

try
{
icHashCode = GetInternalConnectionHashCode( conn );
if ( 0 == icHashCode )
{
failedTrackDisposeConnection++;
return;
}

lock( connectionHash )
{
connectionHash.Remove( icHashCode );
successTrackDisposeConnection++;
}
}
catch ( Exception )
{
failedTrackDisposeConnection++;
}
}
// Returns stack information in string format for specific connection.
public string GetStackInformation( object conn )
{
int icHashCode;
string s;

try
{
icHashCode = GetInternalConnectionHashCode( conn );
if ( 0 == icHashCode ) return "";

lock( connectionHash )
{
s = (string) connectionHash[icHashCode];
}
return s;
}
catch ( Exception )
{
return "";
}
}
// Returns a string array with all of the currently opened connection stacks.
public string [] GetAllOpenConnectionStacks()
{
int i;
string [] s = null;
ArrayList arrValues = null;

try
{
lock (connectionHash)
{
s = new string[connectionHash.Count];
arrValues = new ArrayList(connectionHash.Values);
for ( i=0; i<arrValues.Count; i++ )
{
sIdea = (string) arrValuesIdea;
}
}
return s;
}
catch ( Exception )
{
return null;
}
}

// Internal helper function to get hash code of connection's internalConnection member variable.
private int GetInternalConnectionHashCode( object conn )
{
Type t;
object oic;

// Get internal connection object handle.
try
{
if ( null == conn )
{
failedNullInternalConnection++;
return 0;
}
t = conn.GetType();
if ( null == t )
{
failedNullInternalConnection++;
return 0;
}

// Cool thing about this is it works for both SqlConnection and OracleConnection classes.
// Both of these classes use _internalConnection member variable to store true connection object.
oic = t.InvokeMember( "_internalConnection", BINDING_FLAGS, null, conn, null );

if ( null == oic )
{
failedNullInternalConnection++;
return 0;
}

// Return hashcode.
return oic.GetHashCode();

}
catch( Exception )
{
failedNullInternalConnection++;
return 0;
}
}
}
}



 
 
VMazur





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

Have you checked that connections are actually closed as I suggested in my post

 
 
Matt Neerincx





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

Customers run into this over and over and over and over and over and over.

What we need is built in support for debugging this issue in the classes, I will file a bug internally to see what can be done.



 
 
JohnGalt





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

I ran a profiler on the code and found that although the connection was being closed by the using, it didn't actually close because of a reference in an undisposed object to the connection. As soon as that object was also disposed (i.e. the connection freed in it) then the connection has closed properly and the pool is working the way it's supposed to now.

Thanks for everyone's help!

And yes, this really is not fun. If there was a way to "profile" the connections and see all of the objects using them at any given point, then it would be easy to find these and fix them!


 
 
Matt Neerincx





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

Actually there is a way to do this, but it requires a degree in rocket science.

If you download and install the Windows De**** you can attach to the process then use the SOS de**** extension (located in C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll) to dump out .NET objects and then run !gcroot on the connections to see who is holding a reference.

There are 3rd party tools that make this simpler (I think they are still around), like SciTech's .NET profiler as well.

The CLR team might have some new nifty tools as well, I think they were working on improvements to their .NET profiler.

In the Java world we have a few kick ****tools to do this that are pretty simple to use, for example IBM's HeapRoots tool is nice. But no nice real time object browser for .NET Bummer I say, maybe I should quit Microsoft and write one and make a bundle. (G)



 
 
Damiaan





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

As per the documentation, Dispose ALWAYS calls close first on all connections and streams.

True


IDBConnection as an interface impliments Dispose and calls the close method which is on the interface. [cut out]

This is in the docs that talks about iDBConnection in msdn help.



I can not find anything about iDBConnection implementing the Dispose method.
Where do you find this information I looked at IDbConnection Members on Msdn.


 
 
JohnGalt





PostPosted: .NET Framework Data Access and Storage, Connection Pooling not working Top

Really easy way to verify this:

Just create an IdBConnection and then hit . on your keyboard. You'll see that Dispose() is one of the methods which means that it requires all implimentors to support dispose. In the case of SQL Server (and every other provider that I have seen) dispose calls close as well.