Reputation: 1612
In my multi threaded windows service I open connections to the db on each thread, after that I dispose those connections, though the problem is that some are left not closed on the db when I perform query on the sys.sysprocesses table.
I ran two unit tests and saw some weird behavior, in first I ran a loop with 100 tasks, and in everyone of them I open a new connection. After these are finished (I see via WaitAll()) I see that some connections are still hanging in the db. On second unit test, when I run several open/dispose without parallel execution, it disposes them just fine and there are no hanging connections in my db.
The problem is that in my Windows Service, these hanging connections are being added up and in the end there is no more place for new connections and db becomes not usable for the user.
Here are the codes, first is parallel, second is not:
[TestMethod]
public void TestMultiThreadedAccessToExplicitObjectContext()
{
int taskSize = 100;
List<Task> taskList = new List<Task>();
int goodSources = 0;
for (int i = 0; i < taskSize; i++)
{
Task newTask = Task.Factory.StartNew(() =>
{
System.Data.Objects.ObjectContext objectContext = new PersoniteEntities();
objectContext.Connection.Open();
objectContext.Dispose();
Thread.Sleep(1200);
});
taskList.Add(newTask);
}
Task.WaitAll(taskList.ToArray());
GC.Collect();
total += goodSources;
}
[TestMethod]
public void TestMultiThreadedAccessToExplicitObjectContextInline()
{
System.Data.Objects.ObjectContext objectContext1 = new PersoniteEntities();
objectContext1.Connection.Open();
objectContext1.Dispose();
System.Data.Objects.ObjectContext objectContext2 = new PersoniteEntities();
objectContext2.Connection.Open();
objectContext2.Dispose();
System.Data.Objects.ObjectContext objectContext3 = new PersoniteEntities();
objectContext3.Connection.Open();
objectContext3.Dispose();
System.Data.Objects.ObjectContext objectContext4 = new PersoniteEntities();
objectContext4.Connection.Open();
objectContext4.Dispose();
System.Data.Objects.ObjectContext objectContext5 = new PersoniteEntities();
objectContext5.Connection.Open();
objectContext5.Dispose();
}
Thanks
Upvotes: 4
Views: 2740
Reputation: 36035
Assuming that is all the code you are running in the test, you are probably not looking at hanged connections. This is normal behavior with connection pooling.
When you use a single thread .net can reuse the same physical connection as it was already closed from the previous command. With multiple threads, it has to open several physical connections to attend to the parallel requests.
Ps. You should use the using statement instead of the manual .Dispose, as you risk having connections opened longer than expected if the stuff after the open throws an exception. And thus you can run out of connections.
Upvotes: 1
Reputation: 5761
With connection pooling, each worker process (or in this case thread) has it's own connection pool. So if you have Max Pool Size set to say 3, and spawn 100x threads, you potentially can have 300 connections.
More information from someone who had a similar problem:
ODP.NET Connection Pooling Parameters
And some documentation from MS about how connection pools work, and an explantion of connection pool fragmentation:
MSDN - SQL Server Connection Pooling (ADO.NET)
Solution 1
Turn connection pooling off in the connection string
MSDN - Connection String - Pooling
When the value of this key is set to true, any newly created connection will be added to the pool when closed by the application. In a next attempt to open the same connection, that connection will be drawn from the pool. Connections are considered the same if they have the same connection string. Different connections have different connection strings.
The value of this key can be "true", "false", "yes", or "no".
For example:
<add
name="AppEntities"
connectionString="
metadata=res://*/App_Code.AppModel.csdl|res://*/App_Code.AppModel.ssdl|res://*/App_Code.AppModel.msl;
provider=System.Data.SqlClient;
provider connection string="
Data Source=.;
Initial Catalog=Example;
Integrated Security=True;
Pooling=False;
MultipleActiveResultSets=True
""
providerName="System.Data.EntityClient"
/>
Solution 2
It looks like there may be a workaround involving semaphores, detailed here:
Database connection pooling with multi-threaded service
Upvotes: 4