Skip to content

Commit bc145ab

Browse files
committed
Move procedure cache to connection pool. Fixes #415
1 parent 721ae48 commit bc145ab

File tree

2 files changed

+28
-4
lines changed

2 files changed

+28
-4
lines changed

src/MySqlConnector/Core/ConnectionPool.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ public async Task ClearAsync(IOBehavior ioBehavior, CancellationToken cancellati
166166
// increment the generation of the connection pool
167167
Log.Info("{0} clearing connection pool", m_logArguments);
168168
Interlocked.Increment(ref m_generation);
169+
m_procedureCache = null;
169170
RecoverLeakedSessions();
170171
await CleanPoolAsync(ioBehavior, session => session.PoolGeneration != m_generation, false, cancellationToken).ConfigureAwait(false);
171172
}
@@ -179,6 +180,23 @@ public async Task ReapAsync(IOBehavior ioBehavior, CancellationToken cancellatio
179180
await CleanPoolAsync(ioBehavior, session => (DateTime.UtcNow - session.LastReturnedUtc).TotalSeconds >= m_connectionSettings.ConnectionIdleTimeout, true, cancellationToken).ConfigureAwait(false);
180181
}
181182

183+
/// <summary>
184+
/// Returns the stored procedure cache for this <see cref="ConnectionPool"/>, lazily creating it on demand.
185+
/// This method may return a different object after <see cref="ClearAsync"/> has been called. The returned
186+
/// object is shared between multiple threads and is only safe to use after taking a <c>lock</c> on the
187+
/// object itself.
188+
/// </summary>
189+
public Dictionary<string, CachedProcedure> GetProcedureCache()
190+
{
191+
var procedureCache = m_procedureCache;
192+
if (procedureCache == null)
193+
{
194+
var newProcedureCache = new Dictionary<string, CachedProcedure>();
195+
procedureCache = Interlocked.CompareExchange(ref m_procedureCache, newProcedureCache, null) ?? newProcedureCache;
196+
}
197+
return procedureCache;
198+
}
199+
182200
/// <summary>
183201
/// Examines all the <see cref="ServerSession"/> objects in <see cref="m_leasedSessions"/> to determine if any
184202
/// have an owning <see cref="MySqlConnection"/> that has been garbage-collected. If so, assumes that the connection
@@ -461,5 +479,6 @@ public IEnumerable<string> LoadBalance(IReadOnlyList<string> hosts)
461479
readonly object[] m_logArguments;
462480
uint m_lastRecoveryTime;
463481
int m_lastSessionId;
482+
Dictionary<string, CachedProcedure> m_procedureCache;
464483
}
465484
}

src/MySqlConnector/MySql.Data.MySqlClient/MySqlConnection.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -302,14 +302,19 @@ internal async Task<CachedProcedure> GetCachedProcedure(IOBehavior ioBehavior, s
302302
if (m_session.ServerVersion.Version < ServerVersions.SupportsProcedureCache)
303303
return null;
304304

305-
if (m_cachedProcedures == null)
306-
m_cachedProcedures = new Dictionary<string, CachedProcedure>();
305+
var cachedProcedures = m_session.Pool?.GetProcedureCache() ?? m_cachedProcedures;
306+
if (cachedProcedures == null)
307+
cachedProcedures = m_cachedProcedures = new Dictionary<string, CachedProcedure>();
307308

308309
var normalized = NormalizedSchema.MustNormalize(name, Database);
309-
if (!m_cachedProcedures.TryGetValue(normalized.FullyQualified, out var cachedProcedure))
310+
CachedProcedure cachedProcedure;
311+
lock (cachedProcedures)
312+
cachedProcedures.TryGetValue(normalized.FullyQualified, out cachedProcedure);
313+
if (cachedProcedure == null)
310314
{
311315
cachedProcedure = await CachedProcedure.FillAsync(ioBehavior, this, normalized.Schema, normalized.Component, cancellationToken).ConfigureAwait(false);
312-
m_cachedProcedures[normalized.FullyQualified] = cachedProcedure;
316+
lock (cachedProcedures)
317+
cachedProcedures[normalized.FullyQualified] = cachedProcedure;
313318
}
314319
return cachedProcedure;
315320
}

0 commit comments

Comments
 (0)