Skip to content

Commit dc6f058

Browse files
committed
Add logging API. Fixes #390
1 parent c0e6ab3 commit dc6f058

13 files changed

+440
-30
lines changed

src/MySqlConnector/Core/ConnectionPool.cs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
using System.Threading;
55
using System.Threading.Tasks;
66
using MySql.Data.MySqlClient;
7+
using MySqlConnector.Logging;
78
using MySqlConnector.Protocol.Serialization;
9+
using MySqlConnector.Utilities;
810

911
namespace MySqlConnector.Core
1012
{
1113
internal sealed class ConnectionPool
1214
{
15+
public int Id { get; }
16+
1317
public async Task<ServerSession> GetSessionAsync(MySqlConnection connection, IOBehavior ioBehavior, CancellationToken cancellationToken)
1418
{
1519
cancellationToken.ThrowIfCancellationRequested();
@@ -18,12 +22,16 @@ public async Task<ServerSession> GetSessionAsync(MySqlConnection connection, IOB
1822
// check at most once per second (although this isn't enforced via a mutex so multiple threads might block
1923
// on the lock in RecoverLeakedSessions in high-concurrency situations
2024
if (m_sessionSemaphore.CurrentCount == 0 && unchecked(((uint) Environment.TickCount) - m_lastRecoveryTime) >= 1000u)
25+
{
26+
Log.Warn("{0} is empty; recovering leaked sessions", m_logArguments);
2127
RecoverLeakedSessions();
28+
}
2229

2330
if (m_connectionSettings.MinimumPoolSize > 0)
2431
await CreateMinimumPooledSessions(ioBehavior, cancellationToken).ConfigureAwait(false);
2532

2633
// wait for an open slot (until the cancellationToken is cancelled, which is typically due to timeout)
34+
Log.Debug("{0} waiting for an available session", m_logArguments);
2735
if (ioBehavior == IOBehavior.Asynchronous)
2836
await m_sessionSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
2937
else
@@ -43,10 +51,12 @@ public async Task<ServerSession> GetSessionAsync(MySqlConnection connection, IOB
4351
}
4452
if (session != null)
4553
{
54+
Log.Debug("{0} found an existing session; checking it for validity", m_logArguments);
4655
bool reuseSession;
4756

4857
if (session.PoolGeneration != m_generation)
4958
{
59+
Log.Debug("{0} discarding session due to wrong generation", m_logArguments);
5060
reuseSession = false;
5161
}
5262
else
@@ -64,26 +74,41 @@ public async Task<ServerSession> GetSessionAsync(MySqlConnection connection, IOB
6474
if (!reuseSession)
6575
{
6676
// session is either old or cannot communicate with the server
77+
Log.Warn("{0} Session{1} is unusable; destroying it", m_logArguments[0], session.Id);
6778
AdjustHostConnectionCount(session, -1);
6879
await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
6980
}
7081
else
7182
{
7283
// pooled session is ready to be used; return it
7384
session.OwningConnection = new WeakReference<MySqlConnection>(connection);
85+
int leasedSessionsCountPooled;
7486
lock (m_leasedSessions)
87+
{
7588
m_leasedSessions.Add(session.Id, session);
89+
leasedSessionsCountPooled = m_leasedSessions.Count;
90+
}
91+
if (Log.IsDebugEnabled())
92+
Log.Debug("{0} returning pooled Session{1} to caller; m_leasedSessions.Count={2}", m_logArguments[0], session.Id, leasedSessionsCountPooled);
7693
return session;
7794
}
7895
}
7996

8097
// create a new session
81-
session = new ServerSession(this, m_generation, Interlocked.Increment(ref m_lastId));
98+
session = new ServerSession(this, m_generation, Interlocked.Increment(ref m_lastSessionId));
99+
if (Log.IsInfoEnabled())
100+
Log.Info("{0} no pooled session available; created new Session{1}", m_logArguments[0], session.Id);
82101
await session.ConnectAsync(m_connectionSettings, m_loadBalancer, ioBehavior, cancellationToken).ConfigureAwait(false);
83102
AdjustHostConnectionCount(session, 1);
84103
session.OwningConnection = new WeakReference<MySqlConnection>(connection);
104+
int leasedSessionsCountNew;
85105
lock (m_leasedSessions)
106+
{
86107
m_leasedSessions.Add(session.Id, session);
108+
leasedSessionsCountNew = m_leasedSessions.Count;
109+
}
110+
if (Log.IsDebugEnabled())
111+
Log.Debug("{0} returning new Session{1} to caller; m_leasedSessions.Count={2}", m_logArguments[0], session.Id, leasedSessionsCountNew);
87112
return session;
88113
}
89114
catch
@@ -110,6 +135,9 @@ private bool SessionIsHealthy(ServerSession session)
110135

111136
public void Return(ServerSession session)
112137
{
138+
if (Log.IsDebugEnabled())
139+
Log.Debug("{0} receiving Session{1} back", m_logArguments[0], session.Id);
140+
113141
try
114142
{
115143
lock (m_leasedSessions)
@@ -122,6 +150,7 @@ public void Return(ServerSession session)
122150
}
123151
else
124152
{
153+
Log.Warn("{0} received invalid Session{1}; destroying it", m_logArguments[0], session.Id);
125154
AdjustHostConnectionCount(session, -1);
126155
session.DisposeAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
127156
}
@@ -135,13 +164,15 @@ public void Return(ServerSession session)
135164
public async Task ClearAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
136165
{
137166
// increment the generation of the connection pool
167+
Log.Info("{0} clearing connection pool", m_logArguments);
138168
Interlocked.Increment(ref m_generation);
139169
RecoverLeakedSessions();
140170
await CleanPoolAsync(ioBehavior, session => session.PoolGeneration != m_generation, false, cancellationToken).ConfigureAwait(false);
141171
}
142172

143173
public async Task ReapAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
144174
{
175+
Log.Debug("{0} reaping connection pool", m_logArguments);
145176
RecoverLeakedSessions();
146177
if (m_connectionSettings.ConnectionIdleTimeout == 0)
147178
return;
@@ -165,6 +196,10 @@ private void RecoverLeakedSessions()
165196
recoveredSessions.Add(session);
166197
}
167198
}
199+
if (recoveredSessions.Count == 0)
200+
Log.Debug("{0} recovered no sessions", m_logArguments);
201+
else
202+
Log.Warn("{0} recovered {1} sessions", m_logArguments[0], recoveredSessions.Count);
168203
foreach (var session in recoveredSessions)
169204
session.ReturnToPool();
170205
}
@@ -218,6 +253,7 @@ private async Task CleanPoolAsync(IOBehavior ioBehavior, Func<ServerSession, boo
218253
if (shouldCleanFn(session))
219254
{
220255
// session should be cleaned; dispose it and keep iterating
256+
Log.Info("{0} found Session{1} to clean up", m_logArguments[0], session.Id);
221257
await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
222258
}
223259
else
@@ -266,7 +302,8 @@ private async Task CreateMinimumPooledSessions(IOBehavior ioBehavior, Cancellati
266302

267303
try
268304
{
269-
var session = new ServerSession(this, m_generation, Interlocked.Increment(ref m_lastId));
305+
var session = new ServerSession(this, m_generation, Interlocked.Increment(ref m_lastSessionId));
306+
Log.Info("{0} created Session{1} to reach minimum pool size", m_logArguments[0], session.Id);
270307
await session.ConnectAsync(m_connectionSettings, m_loadBalancer, ioBehavior, cancellationToken).ConfigureAwait(false);
271308
AdjustHostConnectionCount(session, 1);
272309
lock (m_sessions)
@@ -343,7 +380,7 @@ private ConnectionPool(ConnectionSettings cs)
343380
m_cleanSemaphore = new SemaphoreSlim(1);
344381
m_sessionSemaphore = new SemaphoreSlim(cs.MaximumPoolSize);
345382
m_sessions = new LinkedList<ServerSession>();
346-
m_leasedSessions = new Dictionary<int, ServerSession>();
383+
m_leasedSessions = new Dictionary<string, ServerSession>();
347384
if (cs.LoadBalance == MySqlLoadBalance.LeastConnections)
348385
{
349386
m_hostSessions = new Dictionary<string, int>();
@@ -355,6 +392,14 @@ private ConnectionPool(ConnectionSettings cs)
355392
cs.LoadBalance == MySqlLoadBalance.Random ? RandomLoadBalancer.Instance :
356393
cs.LoadBalance == MySqlLoadBalance.LeastConnections ? new LeastConnectionsLoadBalancer(this) :
357394
(ILoadBalancer) new RoundRobinLoadBalancer();
395+
396+
Id = Interlocked.Increment(ref s_poolId);
397+
m_logArguments = new object[] { "Pool{0}".FormatInvariant(Id) };
398+
if (Log.IsInfoEnabled())
399+
{
400+
var csb = new MySqlConnectionStringBuilder(cs.ConnectionString);
401+
Log.Info("{0} creating new connection pool for {1}", m_logArguments[0], csb.GetConnectionString(includePassword: false));
402+
}
358403
}
359404

360405
private void AdjustHostConnectionCount(ServerSession session, int delta)
@@ -379,6 +424,7 @@ public IEnumerable<string> LoadBalance(IReadOnlyList<string> hosts)
379424
readonly ConnectionPool m_pool;
380425
}
381426

427+
static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(ConnectionPool));
382428
static readonly ReaderWriterLockSlim s_poolLock = new ReaderWriterLockSlim();
383429
static readonly Dictionary<string, ConnectionPool> s_pools = new Dictionary<string, ConnectionPool>();
384430
#if DEBUG
@@ -402,15 +448,18 @@ public IEnumerable<string> LoadBalance(IReadOnlyList<string> hosts)
402448
}
403449
});
404450

451+
static int s_poolId;
452+
405453
int m_generation;
406454
readonly SemaphoreSlim m_cleanSemaphore;
407455
readonly SemaphoreSlim m_sessionSemaphore;
408456
readonly LinkedList<ServerSession> m_sessions;
409457
readonly ConnectionSettings m_connectionSettings;
410-
readonly Dictionary<int, ServerSession> m_leasedSessions;
458+
readonly Dictionary<string, ServerSession> m_leasedSessions;
411459
readonly ILoadBalancer m_loadBalancer;
412460
readonly Dictionary<string, int> m_hostSessions;
413-
int m_lastId;
461+
readonly object[] m_logArguments;
414462
uint m_lastRecoveryTime;
463+
int m_lastSessionId;
415464
}
416465
}

0 commit comments

Comments
 (0)