Skip to content

Commit ae00599

Browse files
committed
Add ConnectionOpenedCallback. Fixes #1508
This differs from the existing StateChanged event in that: - it supports an async callback - it's only invoked when a connection is opened - it provides information about new vs existing and whether the connection was reset Signed-off-by: Bradley Grainger <[email protected]>
1 parent 34621d4 commit ae00599

File tree

5 files changed

+67
-3
lines changed

5 files changed

+67
-3
lines changed

src/MySqlConnector/Core/ServerSession.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ public ServerSession(ILogger logger, IConnectionPoolMetadata pool)
6565
public bool ProcAccessDenied { get; set; }
6666
public ICollection<KeyValuePair<string, object?>> ActivityTags => m_activityTags;
6767
public MySqlDataReader DataReader { get; set; }
68+
public MySqlConnectionOpenedConditions Conditions { get; private set; }
6869

6970
public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? owningConnection)
7071
{
7172
Log.ReturningToPool(m_logger, Id, Pool?.Id ?? 0);
73+
Conditions = MySqlConnectionOpenedConditions.None;
7274
LastReturnedTimestamp = Stopwatch.GetTimestamp();
7375
if (Pool is null)
7476
return default;
@@ -414,6 +416,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
414416
}
415417
}
416418

419+
Conditions = MySqlConnectionOpenedConditions.New;
417420
var connected = cs.ConnectionProtocol switch
418421
{
419422
MySqlConnectionProtocol.Sockets => await OpenTcpSocketAsync(cs, loadBalancer ?? throw new ArgumentNullException(nameof(loadBalancer)), activity, ioBehavior, cancellationToken).ConfigureAwait(false),
@@ -747,6 +750,7 @@ public static async ValueTask<ServerSession> ConnectAndRedirectAsync(ILogger con
747750
public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConnection connection, IOBehavior ioBehavior, CancellationToken cancellationToken)
748751
{
749752
VerifyState(State.Connected);
753+
Conditions |= MySqlConnectionOpenedConditions.Reset;
750754

751755
try
752756
{
@@ -829,6 +833,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
829833
Log.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "SocketException");
830834
}
831835

836+
Conditions |= ~MySqlConnectionOpenedConditions.Reset;
832837
return false;
833838
}
834839

src/MySqlConnector/MySqlConnection.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,9 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
582582

583583
if (m_connectionSettings.AutoEnlist && System.Transactions.Transaction.Current is not null)
584584
EnlistTransaction(System.Transactions.Transaction.Current);
585+
586+
if (ConnectionOpenedCallback is { } connectionOpenedCallback)
587+
await connectionOpenedCallback(this, m_session.Conditions).ConfigureAwait(false);
585588
}
586589
catch (Exception ex) when (activity is { IsAllDataRequested: true })
587590
{
@@ -917,6 +920,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
917920

918921
using var connection = CloneWith(csb.ConnectionString);
919922
connection.m_connectionSettings = connectionSettings;
923+
connection.ConnectionOpenedCallback = null; // clear the callback because the user doesn't need to execute any setup logic on this connection
920924
connection.Open();
921925
#if NET6_0_OR_GREATER
922926
var killQuerySql = string.Create(CultureInfo.InvariantCulture, $"KILL QUERY {command.Connection!.ServerThread}");
@@ -992,6 +996,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
992996
internal MySqlTransaction? CurrentTransaction { get; set; }
993997
internal MySqlConnectorLoggingConfiguration LoggingConfiguration { get; }
994998
internal ZstandardPlugin? ZstandardPlugin { get; set; }
999+
internal MySqlConnectionOpenedCallback? ConnectionOpenedCallback { get; set; }
9951000
internal bool AllowLoadLocalInfile => GetInitializedConnectionSettings().AllowLoadLocalInfile;
9961001
internal bool AllowUserVariables => GetInitializedConnectionSettings().AllowUserVariables;
9971002
internal bool AllowZeroDateTime => GetInitializedConnectionSettings().AllowZeroDateTime;
@@ -1142,6 +1147,7 @@ private MySqlConnection(MySqlConnection other, MySqlDataSource? dataSource, stri
11421147
ProvideClientCertificatesCallback = other.ProvideClientCertificatesCallback;
11431148
ProvidePasswordCallback = other.ProvidePasswordCallback;
11441149
RemoteCertificateValidationCallback = other.RemoteCertificateValidationCallback;
1150+
ConnectionOpenedCallback = other.ConnectionOpenedCallback;
11451151
}
11461152

11471153
private void VerifyNotDisposed()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace MySqlConnector;
2+
3+
/// <summary>
4+
/// A callback that is invoked when a new <see cref="MySqlConnection"/> is opened.
5+
/// </summary>
6+
/// <param name="connection">The <see cref="MySqlConnection"/> that was opened.</param>
7+
/// <param name="conditions">Bitflags giving the conditions under which a connection was opened.</param>
8+
/// <returns>A <see cref="ValueTask"/> representing the result of the possibly-asynchronous operation.</returns>
9+
public delegate ValueTask MySqlConnectionOpenedCallback(MySqlConnection connection, MySqlConnectionOpenedConditions conditions);
10+
11+
/// <summary>
12+
/// Bitflags giving the conditions under which a connection was opened.
13+
/// </summary>
14+
[Flags]
15+
public enum MySqlConnectionOpenedConditions
16+
{
17+
/// <summary>
18+
/// No specific conditions apply. This value may be used when an existing pooled connection is reused without being reset.
19+
/// </summary>
20+
None = 0,
21+
22+
/// <summary>
23+
/// A new physical connection to a MySQL Server was opened. This value is mutually exclusive with <see cref="Reset"/>.
24+
/// </summary>
25+
New = 1,
26+
27+
/// <summary>
28+
/// An existing pooled connection to a MySQL Server was reset. This value is mutually exclusive with <see cref="New"/>.
29+
/// </summary>
30+
Reset = 2,
31+
}

src/MySqlConnector/MySqlDataSource.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public sealed class MySqlDataSource : DbDataSource
1919
/// <param name="connectionString">The connection string for the MySQL Server. This parameter is required.</param>
2020
/// <exception cref="ArgumentNullException">Thrown if <paramref name="connectionString"/> is <c>null</c>.</exception>
2121
public MySqlDataSource(string connectionString)
22-
: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, default, default, default)
22+
: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, default, default, default, default)
2323
{
2424
}
2525

@@ -31,7 +31,8 @@ internal MySqlDataSource(string connectionString,
3131
Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? periodicPasswordProvider,
3232
TimeSpan periodicPasswordProviderSuccessRefreshInterval,
3333
TimeSpan periodicPasswordProviderFailureRefreshInterval,
34-
ZstandardPlugin? zstandardPlugin)
34+
ZstandardPlugin? zstandardPlugin,
35+
MySqlConnectionOpenedCallback? connectionOpenedCallback)
3536
{
3637
m_connectionString = connectionString;
3738
LoggingConfiguration = loggingConfiguration;
@@ -40,6 +41,7 @@ internal MySqlDataSource(string connectionString,
4041
m_remoteCertificateValidationCallback = remoteCertificateValidationCallback;
4142
m_logger = loggingConfiguration.DataSourceLogger;
4243
m_zstandardPlugin = zstandardPlugin;
44+
m_connectionOpenedCallback = connectionOpenedCallback;
4345

4446
Pool = ConnectionPool.CreatePool(m_connectionString, LoggingConfiguration, name);
4547
m_id = Interlocked.Increment(ref s_lastId);
@@ -142,6 +144,7 @@ protected override DbConnection CreateDbConnection()
142144
ProvideClientCertificatesCallback = m_clientCertificatesCallback,
143145
ProvidePasswordCallback = m_providePasswordCallback,
144146
RemoteCertificateValidationCallback = m_remoteCertificateValidationCallback,
147+
ConnectionOpenedCallback = m_connectionOpenedCallback,
145148
};
146149
}
147150

@@ -225,6 +228,7 @@ private string ProvidePasswordFromInitialRefreshTask(MySqlProvidePasswordContext
225228
private readonly TimeSpan m_periodicPasswordProviderSuccessRefreshInterval;
226229
private readonly TimeSpan m_periodicPasswordProviderFailureRefreshInterval;
227230
private readonly ZstandardPlugin? m_zstandardPlugin;
231+
private readonly MySqlConnectionOpenedCallback? m_connectionOpenedCallback;
228232
private readonly MySqlProvidePasswordContext? m_providePasswordContext;
229233
private readonly CancellationTokenSource? m_passwordProviderTimerCancellationTokenSource;
230234
private readonly Timer? m_passwordProviderTimer;

src/MySqlConnector/MySqlDataSourceBuilder.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,22 @@ public MySqlDataSourceBuilder UseRemoteCertificateValidationCallback(RemoteCerti
8989
return this;
9090
}
9191

92+
public MySqlDataSourceBuilder UseConnectionOpenedCallback(MySqlConnectionOpenedCallback callback)
93+
{
94+
m_connectionOpenedCallback = callback;
95+
return this;
96+
}
97+
98+
public MySqlDataSourceBuilder UseConnectionOpenedCallback(Action<MySqlConnection, MySqlConnectionOpenedConditions> callback)
99+
{
100+
m_connectionOpenedCallback = (connection, conditions) =>
101+
{
102+
callback(connection, conditions);
103+
return default;
104+
};
105+
return this;
106+
}
107+
92108
/// <summary>
93109
/// Builds a <see cref="MySqlDataSource"/> which is ready for use.
94110
/// </summary>
@@ -104,7 +120,8 @@ public MySqlDataSource Build()
104120
m_periodicPasswordProvider,
105121
m_periodicPasswordProviderSuccessRefreshInterval,
106122
m_periodicPasswordProviderFailureRefreshInterval,
107-
ZstandardPlugin
123+
ZstandardPlugin,
124+
m_connectionOpenedCallback
108125
);
109126
}
110127

@@ -122,4 +139,5 @@ public MySqlDataSource Build()
122139
private Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? m_periodicPasswordProvider;
123140
private TimeSpan m_periodicPasswordProviderSuccessRefreshInterval;
124141
private TimeSpan m_periodicPasswordProviderFailureRefreshInterval;
142+
private MySqlConnectionOpenedCallback? m_connectionOpenedCallback;
125143
}

0 commit comments

Comments
 (0)