Skip to content

Commit ec8d209

Browse files
committed
Use ConcurrentDictionary for connection pool.
This should be faster (especially under contention) than ReaderWriterLockSlim.
1 parent f2255cc commit ec8d209

File tree

1 file changed

+29
-45
lines changed

1 file changed

+29
-45
lines changed

src/MySqlConnector/Core/ConnectionPool.cs

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Threading;
@@ -340,54 +341,39 @@ private async Task CreateMinimumPooledSessions(IOBehavior ioBehavior, Cancellati
340341
public static ConnectionPool GetPool(string connectionString)
341342
{
342343
// check if pool has already been created for this exact connection string
343-
try
344-
{
345-
s_poolLock.EnterReadLock();
346-
if (s_pools.TryGetValue(connectionString, out var pool))
347-
return pool;
348-
}
349-
finally
350-
{
351-
s_poolLock.ExitReadLock();
352-
}
344+
if (s_pools.TryGetValue(connectionString, out var pool))
345+
return pool;
353346

354-
// parse connection string and check for 'Pooling' setting
347+
// parse connection string and check for 'Pooling' setting; return 'null' if pooling is disabled
355348
var connectionStringBuilder = new MySqlConnectionStringBuilder(connectionString);
356349
if (!connectionStringBuilder.Pooling)
350+
{
351+
s_pools.GetOrAdd(connectionString, default(ConnectionPool));
357352
return null;
353+
}
358354

359355
// check for pool using normalized form of connection string
360356
var normalizedConnectionString = connectionStringBuilder.ConnectionString;
361-
try
357+
if (normalizedConnectionString != connectionString && s_pools.TryGetValue(normalizedConnectionString, out pool))
362358
{
363-
s_poolLock.EnterReadLock();
364-
if (s_pools.TryGetValue(normalizedConnectionString, out var pool))
365-
return pool;
366-
// TODO: Add an entry using 'connectionString'?
367-
}
368-
finally
369-
{
370-
s_poolLock.ExitReadLock();
359+
// try to set the pool for the connection string to the canonical pool; if someone else
360+
// beats us to it, just use the existing value
361+
pool = s_pools.GetOrAdd(connectionString, pool);
362+
return pool;
371363
}
372364

365+
// create a new pool and attempt to insert it; if someone else beats us to it, just use their value
373366
var connectionSettings = new ConnectionSettings(connectionStringBuilder);
367+
var newPool = new ConnectionPool(connectionSettings);
368+
pool = s_pools.GetOrAdd(normalizedConnectionString, newPool);
374369

375-
try
376-
{
377-
s_poolLock.EnterWriteLock();
378-
if (!s_pools.TryGetValue(normalizedConnectionString, out var pool))
379-
{
380-
pool = new ConnectionPool(connectionSettings);
381-
s_pools[normalizedConnectionString] = pool;
382-
if (normalizedConnectionString != connectionString)
383-
s_pools[connectionString] = pool;
384-
}
385-
return pool;
386-
}
387-
finally
388-
{
389-
s_poolLock.ExitWriteLock();
390-
}
370+
// if we won the race to create the new pool, also store it under the original connection string
371+
if (pool == newPool && connectionString != normalizedConnectionString)
372+
s_pools.GetOrAdd(connectionString, pool);
373+
else if (pool != newPool && Log.IsInfoEnabled())
374+
Log.Info("{0} was created but will not be used (due to race)", newPool.m_logArguments[0]);
375+
376+
return pool;
391377
}
392378

393379
public static async Task ClearPoolsAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
@@ -404,15 +390,14 @@ public static async Task ReapPoolsAsync(IOBehavior ioBehavior, CancellationToken
404390

405391
private static IReadOnlyList<ConnectionPool> GetAllPools()
406392
{
407-
try
408-
{
409-
s_poolLock.EnterReadLock();
410-
return s_pools.Values.ToList();
411-
}
412-
finally
393+
var pools = new List<ConnectionPool>(s_pools.Count);
394+
var uniquePools = new HashSet<ConnectionPool>();
395+
foreach (var pool in s_pools.Values)
413396
{
414-
s_poolLock.ExitReadLock();
397+
if (pool != null && uniquePools.Add(pool))
398+
pools.Add(pool);
415399
}
400+
return pools;
416401
}
417402

418403
private ConnectionPool(ConnectionSettings cs)
@@ -464,8 +449,7 @@ public IEnumerable<string> LoadBalance(IReadOnlyList<string> hosts)
464449
}
465450

466451
static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(ConnectionPool));
467-
static readonly ReaderWriterLockSlim s_poolLock = new ReaderWriterLockSlim();
468-
static readonly Dictionary<string, ConnectionPool> s_pools = new Dictionary<string, ConnectionPool>();
452+
static readonly ConcurrentDictionary<string, ConnectionPool> s_pools = new ConcurrentDictionary<string, ConnectionPool>();
469453
#if DEBUG
470454
static readonly TimeSpan ReaperInterval = TimeSpan.FromSeconds(1);
471455
#else

0 commit comments

Comments
 (0)