Skip to content

Commit

Permalink
Add MySqlDataSource prototype.
Browse files Browse the repository at this point in the history
Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
  • Loading branch information
bgrainger committed Feb 6, 2022
1 parent 69e8353 commit e14a6a7
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 3 deletions.
17 changes: 17 additions & 0 deletions src/MySqlConnector/Core/ConnectionPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,23 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
return session;
}

public static ConnectionPool? CreatePool(string connectionString)
{
// parse connection string and check for 'Pooling' setting; return 'null' if pooling is disabled
var connectionStringBuilder = new MySqlConnectionStringBuilder(connectionString);
if (!connectionStringBuilder.Pooling)
{
return null;
}

// create a new pool and attempt to insert it; if someone else beats us to it, just use their value
var connectionSettings = new ConnectionSettings(connectionStringBuilder);
var pool = new ConnectionPool(connectionSettings);
pool.StartReaperTask();

return pool;
}

public static ConnectionPool? GetPool(string connectionString)
{
// check single-entry MRU cache for this exact connection string; most applications have just one
Expand Down
43 changes: 43 additions & 0 deletions src/MySqlConnector/DbDataSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace System.Data;

public abstract class DbDataSource
#if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
: IDisposable, IAsyncDisposable
#else
: IDisposable
#endif
{
public abstract string ConnectionString { get; }

protected abstract DbConnection GetDbConnection();

protected virtual DbConnection OpenDbConnection()
{
var connection = GetDbConnection();
connection.Open();

return connection;
}

protected virtual async ValueTask<DbConnection> OpenDbConnectionAsync(CancellationToken cancellationToken = default)
{
var connection = GetDbConnection();
await connection.OpenAsync(cancellationToken);

return connection;
}

protected virtual DbCommand CreateDbCommand() => throw new NotImplementedException();

//// protected virtual DbCommand CreateDbBatch() ...

public DbConnection GetConnection() => GetDbConnection();
public DbConnection OpenConnection() => OpenDbConnection();
public ValueTask<DbConnection> OpenConnectionAsync(CancellationToken cancellationToken = default) => OpenDbConnectionAsync(cancellationToken);

public abstract void Dispose();

#if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
public abstract ValueTask DisposeAsync();
#endif
}
12 changes: 9 additions & 3 deletions src/MySqlConnector/MySqlConnection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net.Security;
Expand All @@ -19,7 +18,7 @@ namespace MySqlConnector;
public sealed class MySqlConnection : DbConnection, ICloneable
{
public MySqlConnection()
: this(default)
: this("")
{
}

Expand All @@ -29,6 +28,12 @@ public MySqlConnection(string? connectionString)
m_connectionString = connectionString ?? "";
}

internal MySqlConnection(MySqlDataSource dataSource)
: this(dataSource.ConnectionString)
{
m_dataSource = dataSource;
}

#pragma warning disable CA2012 // Safe because method completes synchronously
/// <summary>
/// Begins a database transaction.
Expand Down Expand Up @@ -384,7 +389,7 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella

SetState(ConnectionState.Connecting);

var pool = ConnectionPool.GetPool(m_connectionString);
var pool = m_dataSource?.Pool ?? ConnectionPool.GetPool(m_connectionString);
m_connectionSettings ??= pool?.ConnectionSettings ?? new ConnectionSettings(new MySqlConnectionStringBuilder(m_connectionString));

// check if there is an open session (in the current transaction) that can be adopted
Expand Down Expand Up @@ -1101,6 +1106,7 @@ private ConnectionSettings GetConnectionSettings() =>
static readonly object s_lock = new();
static readonly Dictionary<System.Transactions.Transaction, List<EnlistedTransactionBase>> s_transactionConnections = new();

readonly MySqlDataSource? m_dataSource;
string m_connectionString;
ConnectionSettings? m_connectionSettings;
ServerSession? m_session;
Expand Down
79 changes: 79 additions & 0 deletions src/MySqlConnector/MySqlDataSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using MySqlConnector.Core;
using MySqlConnector.Logging;
using MySqlConnector.Protocol.Serialization;

namespace MySqlConnector;

public sealed class MySqlDataSource : DbDataSource
{
public MySqlDataSource(string connectionString)
{
m_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
Pool = ConnectionPool.CreatePool(m_connectionString);
m_id = Interlocked.Increment(ref s_lastId);
m_logArguments = new object?[2] { m_id, null };
if (Pool is not null)
{
m_logArguments[1] = Pool.Id;
Log.Info("DataSource{0} created with Pool {1}", m_logArguments);
}
else
{
Log.Info("DataSource{0} created with no pool", m_logArguments);
}
}

public new MySqlConnection GetConnection() => (MySqlConnection) GetDbConnection();

public new MySqlConnection OpenConnection()
{
var connection = GetConnection();
connection.Open();
return connection;
}

public new async ValueTask<MySqlConnection> OpenConnectionAsync(CancellationToken cancellationToken = default)
{
var connection = GetConnection();
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
return connection;
}

public override string ConnectionString => m_connectionString;

public override void Dispose() => DisposeAsync(IOBehavior.Synchronous).GetAwaiter().GetResult();
#if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
public override ValueTask DisposeAsync() =>
#else
public Task DisposeAsync() =>
#endif
DisposeAsync(IOBehavior.Asynchronous);

#if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
private async ValueTask DisposeAsync(IOBehavior ioBehavior)
#else
private async Task DisposeAsync(IOBehavior ioBehavior)
#endif
{
if (Pool is not null)
await Pool.ClearAsync(ioBehavior, default).ConfigureAwait(false);
m_isDisposed = true;
}

protected override DbConnection GetDbConnection()
{
if (m_isDisposed)
throw new ObjectDisposedException(nameof(MySqlDataSource));
return new MySqlConnection(this);
}

internal ConnectionPool? Pool { get; }

private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(MySqlDataSource));
private static int s_lastId;

private readonly int m_id;
private readonly object?[] m_logArguments;
private readonly string m_connectionString;
private bool m_isDisposed;
}

0 comments on commit e14a6a7

Please sign in to comment.