Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ConnectionOpenedCallback #1515

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/MySqlConnector/Core/ServerSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ public ServerSession(ILogger logger, IConnectionPoolMetadata pool)
public bool ProcAccessDenied { get; set; }
public ICollection<KeyValuePair<string, object?>> ActivityTags => m_activityTags;
public MySqlDataReader DataReader { get; set; }
public MySqlConnectionOpenedConditions Conditions { get; private set; }

public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? owningConnection)
{
Log.ReturningToPool(m_logger, Id, Pool?.Id ?? 0);
Conditions = MySqlConnectionOpenedConditions.None;
LastReturnedTimestamp = Stopwatch.GetTimestamp();
if (Pool is null)
return default;
Expand Down Expand Up @@ -414,6 +416,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
}
}

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

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

Conditions |= ~MySqlConnectionOpenedConditions.Reset;
return false;
}

Expand Down
6 changes: 6 additions & 0 deletions src/MySqlConnector/MySqlConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,9 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella

if (m_connectionSettings.AutoEnlist && System.Transactions.Transaction.Current is not null)
EnlistTransaction(System.Transactions.Transaction.Current);

if (ConnectionOpenedCallback is { } connectionOpenedCallback)
await connectionOpenedCallback(this, m_session.Conditions).ConfigureAwait(false);
}
catch (Exception ex) when (activity is { IsAllDataRequested: true })
{
Expand Down Expand Up @@ -917,6 +920,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)

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

private void VerifyNotDisposed()
Expand Down
31 changes: 31 additions & 0 deletions src/MySqlConnector/MySqlConnectionOpenedCallback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace MySqlConnector;

/// <summary>
/// A callback that is invoked when a new <see cref="MySqlConnection"/> is opened.
/// </summary>
/// <param name="connection">The <see cref="MySqlConnection"/> that was opened.</param>
/// <param name="conditions">Bitflags giving the conditions under which a connection was opened.</param>
/// <returns>A <see cref="ValueTask"/> representing the result of the possibly-asynchronous operation.</returns>
public delegate ValueTask MySqlConnectionOpenedCallback(MySqlConnection connection, MySqlConnectionOpenedConditions conditions);

/// <summary>
/// Bitflags giving the conditions under which a connection was opened.
/// </summary>
[Flags]
public enum MySqlConnectionOpenedConditions
{
/// <summary>
/// No specific conditions apply. This value may be used when an existing pooled connection is reused without being reset.
/// </summary>
None = 0,

/// <summary>
/// A new physical connection to a MySQL Server was opened. This value is mutually exclusive with <see cref="Reset"/>.
/// </summary>
New = 1,

/// <summary>
/// An existing pooled connection to a MySQL Server was reset. This value is mutually exclusive with <see cref="New"/>.
/// </summary>
Reset = 2,
}
8 changes: 6 additions & 2 deletions src/MySqlConnector/MySqlDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public sealed class MySqlDataSource : DbDataSource
/// <param name="connectionString">The connection string for the MySQL Server. This parameter is required.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="connectionString"/> is <c>null</c>.</exception>
public MySqlDataSource(string connectionString)
: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, default, default, default)
: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, default, default, default, default)
{
}

Expand All @@ -31,7 +31,8 @@ internal MySqlDataSource(string connectionString,
Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? periodicPasswordProvider,
TimeSpan periodicPasswordProviderSuccessRefreshInterval,
TimeSpan periodicPasswordProviderFailureRefreshInterval,
ZstandardPlugin? zstandardPlugin)
ZstandardPlugin? zstandardPlugin,
MySqlConnectionOpenedCallback? connectionOpenedCallback)
{
m_connectionString = connectionString;
LoggingConfiguration = loggingConfiguration;
Expand All @@ -40,6 +41,7 @@ internal MySqlDataSource(string connectionString,
m_remoteCertificateValidationCallback = remoteCertificateValidationCallback;
m_logger = loggingConfiguration.DataSourceLogger;
m_zstandardPlugin = zstandardPlugin;
m_connectionOpenedCallback = connectionOpenedCallback;

Pool = ConnectionPool.CreatePool(m_connectionString, LoggingConfiguration, name);
m_id = Interlocked.Increment(ref s_lastId);
Expand Down Expand Up @@ -142,6 +144,7 @@ protected override DbConnection CreateDbConnection()
ProvideClientCertificatesCallback = m_clientCertificatesCallback,
ProvidePasswordCallback = m_providePasswordCallback,
RemoteCertificateValidationCallback = m_remoteCertificateValidationCallback,
ConnectionOpenedCallback = m_connectionOpenedCallback,
};
}

Expand Down Expand Up @@ -225,6 +228,7 @@ private string ProvidePasswordFromInitialRefreshTask(MySqlProvidePasswordContext
private readonly TimeSpan m_periodicPasswordProviderSuccessRefreshInterval;
private readonly TimeSpan m_periodicPasswordProviderFailureRefreshInterval;
private readonly ZstandardPlugin? m_zstandardPlugin;
private readonly MySqlConnectionOpenedCallback? m_connectionOpenedCallback;
private readonly MySqlProvidePasswordContext? m_providePasswordContext;
private readonly CancellationTokenSource? m_passwordProviderTimerCancellationTokenSource;
private readonly Timer? m_passwordProviderTimer;
Expand Down
20 changes: 19 additions & 1 deletion src/MySqlConnector/MySqlDataSourceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,22 @@ public MySqlDataSourceBuilder UseRemoteCertificateValidationCallback(RemoteCerti
return this;
}

public MySqlDataSourceBuilder UseConnectionOpenedCallback(MySqlConnectionOpenedCallback callback)
{
m_connectionOpenedCallback = callback;
return this;
}

public MySqlDataSourceBuilder UseConnectionOpenedCallback(Action<MySqlConnection, MySqlConnectionOpenedConditions> callback)
{
m_connectionOpenedCallback = (connection, conditions) =>
{
callback(connection, conditions);
return default;
};
return this;
}

/// <summary>
/// Builds a <see cref="MySqlDataSource"/> which is ready for use.
/// </summary>
Expand All @@ -104,7 +120,8 @@ public MySqlDataSource Build()
m_periodicPasswordProvider,
m_periodicPasswordProviderSuccessRefreshInterval,
m_periodicPasswordProviderFailureRefreshInterval,
ZstandardPlugin
ZstandardPlugin,
m_connectionOpenedCallback
);
}

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