Skip to content

Commit a3c4d70

Browse files
authored
Merge pull request #445 from bgrainger/allocations
Reduce allocations in common paths.
2 parents 6ad155b + 6b47ff9 commit a3c4d70

File tree

6 files changed

+61
-32
lines changed

6 files changed

+61
-32
lines changed

src/MySqlConnector/Core/ServerSession.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
197197
{
198198
Log.Info("{0} sending QUIT command", m_logArguments);
199199
m_payloadHandler.StartNewConversation();
200-
await m_payloadHandler.WritePayloadAsync(QuitPayload.Create().ArraySegment, ioBehavior).ConfigureAwait(false);
200+
await m_payloadHandler.WritePayloadAsync(QuitPayload.Instance.ArraySegment, ioBehavior).ConfigureAwait(false);
201201
}
202202
catch (IOException)
203203
{
@@ -319,13 +319,12 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, IOBehavio
319319
{
320320
m_logArguments[1] = ServerVersion.OriginalString;
321321
Log.Debug("{0} ServerVersion {1} supports reset connection; sending reset connection request", m_logArguments);
322-
await SendAsync(ResetConnectionPayload.Create(), ioBehavior, cancellationToken).ConfigureAwait(false);
322+
await SendAsync(ResetConnectionPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
323323
var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
324324
OkPayload.Create(payload);
325325

326326
// the "reset connection" packet also resets the connection charset, so we need to change that back to our default
327-
payload = QueryPayload.Create("SET NAMES utf8mb4 COLLATE utf8mb4_bin;");
328-
await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
327+
await SendAsync(s_setNamesUtf8mb4Payload, ioBehavior, cancellationToken).ConfigureAwait(false);
329328
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
330329
OkPayload.Create(payload);
331330
}
@@ -515,7 +514,7 @@ public async ValueTask<bool> TryPingAsync(IOBehavior ioBehavior, CancellationTok
515514
try
516515
{
517516
Log.Debug("{0} pinging server", m_logArguments);
518-
await SendAsync(PingPayload.Create(), ioBehavior, cancellationToken).ConfigureAwait(false);
517+
await SendAsync(PingPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
519518
var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
520519
OkPayload.Create(payload);
521520
Log.Info("{0} successfully pinged server", m_logArguments);
@@ -1151,6 +1150,7 @@ private enum State
11511150
static int s_lastId;
11521151
static byte[] s_connectionAttributes;
11531152
static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(ServerSession));
1153+
static readonly PayloadData s_setNamesUtf8mb4Payload = QueryPayload.Create("SET NAMES utf8mb4 COLLATE utf8mb4_bin;");
11541154

11551155
readonly object m_lock;
11561156
readonly object[] m_logArguments;

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

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -377,35 +377,45 @@ private async ValueTask<ServerSession> CreateSessionAsync(IOBehavior? ioBehavior
377377
m_connectionSettings = pool?.ConnectionSettings ?? new ConnectionSettings(new MySqlConnectionStringBuilder(m_connectionString));
378378
var actualIOBehavior = ioBehavior ?? (m_connectionSettings.ForceSynchronous ? IOBehavior.Synchronous : IOBehavior.Asynchronous);
379379

380-
var connectTimeout = m_connectionSettings.ConnectionTimeout == 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromMilliseconds(m_connectionSettings.ConnectionTimeoutMilliseconds);
381-
using (var timeoutSource = new CancellationTokenSource(connectTimeout))
382-
using (var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutSource.Token))
380+
CancellationTokenSource timeoutSource = null;
381+
CancellationTokenSource linkedSource = null;
382+
try
383383
{
384-
try
384+
// the cancellation token for connection is controlled by 'cancellationToken' (if it can be cancelled), ConnectionTimeout
385+
// (from the connection string, if non-zero), or a combination of both
386+
if (m_connectionSettings.ConnectionTimeout != 0)
387+
timeoutSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(m_connectionSettings.ConnectionTimeoutMilliseconds));
388+
if (cancellationToken.CanBeCanceled && timeoutSource != null)
389+
linkedSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutSource.Token);
390+
var connectToken = linkedSource?.Token ?? timeoutSource?.Token ?? cancellationToken;
391+
392+
// get existing session from the pool if possible
393+
if (pool != null)
385394
{
386-
// get existing session from the pool if possible
387-
if (pool != null)
388-
{
389-
// this returns an open session
390-
return await pool.GetSessionAsync(this, actualIOBehavior, linkedSource.Token).ConfigureAwait(false);
391-
}
392-
else
393-
{
394-
// only "fail over" and "random" load balancers supported without connection pooling
395-
var loadBalancer = m_connectionSettings.LoadBalance == MySqlLoadBalance.Random && m_connectionSettings.HostNames.Count > 1 ?
396-
RandomLoadBalancer.Instance : FailOverLoadBalancer.Instance;
397-
398-
var session = new ServerSession();
399-
Log.Info("Created new non-pooled Session{0}", session.Id);
400-
await session.ConnectAsync(m_connectionSettings, loadBalancer, actualIOBehavior, linkedSource.Token).ConfigureAwait(false);
401-
return session;
402-
}
395+
// this returns an open session
396+
return await pool.GetSessionAsync(this, actualIOBehavior, connectToken).ConfigureAwait(false);
403397
}
404-
catch (OperationCanceledException ex) when (timeoutSource.IsCancellationRequested)
398+
else
405399
{
406-
throw new MySqlException("Connect Timeout expired.", ex);
400+
// only "fail over" and "random" load balancers supported without connection pooling
401+
var loadBalancer = m_connectionSettings.LoadBalance == MySqlLoadBalance.Random && m_connectionSettings.HostNames.Count > 1 ?
402+
RandomLoadBalancer.Instance : FailOverLoadBalancer.Instance;
403+
404+
var session = new ServerSession();
405+
Log.Info("Created new non-pooled Session{0}", session.Id);
406+
await session.ConnectAsync(m_connectionSettings, loadBalancer, actualIOBehavior, connectToken).ConfigureAwait(false);
407+
return session;
407408
}
408409
}
410+
catch (OperationCanceledException ex) when (timeoutSource.IsCancellationRequested)
411+
{
412+
throw new MySqlException("Connect Timeout expired.", ex);
413+
}
414+
finally
415+
{
416+
linkedSource?.Dispose();
417+
timeoutSource?.Dispose();
418+
}
409419
}
410420

411421
internal bool SslIsEncrypted => m_session.SslIsEncrypted;
@@ -422,7 +432,12 @@ internal void SetState(ConnectionState newState)
422432
{
423433
var previousState = m_connectionState;
424434
m_connectionState = newState;
425-
OnStateChange(new StateChangeEventArgs(previousState, newState));
435+
var eventArgs =
436+
previousState == ConnectionState.Closed && newState == ConnectionState.Connecting ? s_stateChangeClosedConnecting :
437+
previousState == ConnectionState.Connecting && newState == ConnectionState.Open ? s_stateChangeConnectingOpen :
438+
previousState == ConnectionState.Open && newState == ConnectionState.Closed ? s_stateChangeOpenClosed :
439+
new StateChangeEventArgs(previousState, newState);
440+
OnStateChange(eventArgs);
426441
}
427442
}
428443

@@ -487,6 +502,9 @@ private ConnectionSettings GetConnectionSettings()
487502
}
488503

489504
static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(MySqlConnection));
505+
static readonly StateChangeEventArgs s_stateChangeClosedConnecting = new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Connecting);
506+
static readonly StateChangeEventArgs s_stateChangeConnectingOpen = new StateChangeEventArgs(ConnectionState.Connecting, ConnectionState.Open);
507+
static readonly StateChangeEventArgs s_stateChangeOpenClosed = new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed);
490508

491509
string m_connectionString;
492510
ConnectionSettings m_connectionSettings;

src/MySqlConnector/Protocol/Payloads/OkPayload.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ public static OkPayload Create(PayloadData payload, bool deprecateEof)
6868
// ignore human-readable string in both cases
6969
}
7070

71+
if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && newSchema == null)
72+
{
73+
if (serverStatus == ServerStatus.AutoCommit)
74+
return s_autoCommitOk;
75+
if (serverStatus == (ServerStatus.AutoCommit | ServerStatus.SessionStateChanged))
76+
return s_autoCommitSessionStateChangedOk;
77+
}
78+
7179
return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, newSchema);
7280
}
7381

@@ -79,5 +87,8 @@ private OkPayload(int affectedRowCount, ulong lastInsertId, ServerStatus serverS
7987
WarningCount = warningCount;
8088
NewSchema = newSchema;
8189
}
90+
91+
static readonly OkPayload s_autoCommitOk = new OkPayload(0, 0, ServerStatus.AutoCommit, 0, null);
92+
static readonly OkPayload s_autoCommitSessionStateChangedOk = new OkPayload(0, 0, ServerStatus.AutoCommit | ServerStatus.SessionStateChanged, 0, null);
8293
}
8394
}

src/MySqlConnector/Protocol/Payloads/PingPayload.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ namespace MySqlConnector.Protocol.Payloads
22
{
33
internal sealed class PingPayload
44
{
5-
public static PayloadData Create() => new PayloadData(new[] { (byte) CommandKind.Ping });
5+
public static PayloadData Instance { get; } = new PayloadData(new[] { (byte) CommandKind.Ping });
66
}
77
}

src/MySqlConnector/Protocol/Payloads/QuitPayload.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ namespace MySqlConnector.Protocol.Payloads
22
{
33
internal sealed class QuitPayload
44
{
5-
public static PayloadData Create() => new PayloadData(new[] { (byte) CommandKind.Quit });
5+
public static PayloadData Instance { get; } = new PayloadData(new[] { (byte) CommandKind.Quit });
66
}
77
}

src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ namespace MySqlConnector.Protocol.Payloads
22
{
33
internal sealed class ResetConnectionPayload
44
{
5-
public static PayloadData Create() => new PayloadData(new[] { (byte) CommandKind.ResetConnection });
5+
public static PayloadData Instance { get; } = new PayloadData(new[] { (byte) CommandKind.ResetConnection });
66
}
77
}

0 commit comments

Comments
 (0)