Skip to content

Commit

Permalink
Merge master into ssl.
Browse files Browse the repository at this point in the history
Conflicts:
	.ci/config/config.compression+ssl.json
	.ci/config/config.compression.json
	.ci/config/config.json
	.ci/config/config.ssl.json
	azure-pipelines.yml
	src/MySqlConnector/Core/ConnectionPool.cs
	src/MySqlConnector/Core/ServerSession.cs
	src/MySqlConnector/Logging/Log.cs
	src/MySqlConnector/MySqlConnection.cs
	src/MySqlConnector/Protocol/Payloads/OkPayload.cs
	tests/IntegrationTests/RedirectionTests.cs
	tests/IntegrationTests/ServerFeatures.cs

Signed-off-by: Bradley Grainger <[email protected]>
  • Loading branch information
bgrainger committed Jul 28, 2024
2 parents 89beadf + 6e45dcd commit 098bd5b
Show file tree
Hide file tree
Showing 12 changed files with 156 additions and 124 deletions.
2 changes: 1 addition & 1 deletion .ci/config/config.compression+ssl.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
"PasswordlessUser": "no_password",
"SecondaryDatabase": "testdb2",
"UnsupportedFeatures": "RsaEncryption,CachingSha2Password,Tls12,Tls13,UuidToBin,TlsFingerprintValidation",
"UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin",
"MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
"MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
"CertificatesPath": "../../../../.ci/server/certs"
Expand Down
2 changes: 1 addition & 1 deletion .ci/config/config.compression.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
"PasswordlessUser": "no_password",
"SecondaryDatabase": "testdb2",
"UnsupportedFeatures": "Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket,ZeroDateTime,TlsFingerprintValidation",
"UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
"MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
"MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
}
Expand Down
2 changes: 1 addition & 1 deletion .ci/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
"PasswordlessUser": "no_password",
"SecondaryDatabase": "testdb2",
"UnsupportedFeatures": "Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket,ZeroDateTime,TlsFingerprintValidation",
"UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
"MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
"MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
}
Expand Down
2 changes: 1 addition & 1 deletion .ci/config/config.ssl.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
"PasswordlessUser": "no_password",
"SecondaryDatabase": "testdb2",
"UnsupportedFeatures": "RsaEncryption,CachingSha2Password,Tls12,Tls13,UuidToBin,TlsFingerprintValidation",
"UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin",
"MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
"MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV",
"CertificatesPath": "../../../../.ci/server/certs"
Expand Down
14 changes: 7 additions & 7 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ jobs:
arguments: '-c Release --no-restore'
testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net472/net8.0', 'No SSL') }}
env:
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket'
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,UnixDomainSocket'
DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True'

- job: windows_integration_tests_2
Expand Down Expand Up @@ -174,7 +174,7 @@ jobs:
arguments: '-c Release --no-restore'
testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net6.0', 'No SSL') }}
env:
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket'
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,UnixDomainSocket'
DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'

- job: linux_integration_tests
Expand All @@ -187,23 +187,23 @@ jobs:
'MySQL 8.0':
image: 'mysql:8.0'
connectionStringExtra: 'AllowPublicKeyRetrieval=True'
unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime,Redirection,TlsFingerprintValidation'
unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
'MySQL 8.4':
image: 'mysql:8.4'
connectionStringExtra: 'AllowPublicKeyRetrieval=True'
unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime,Redirection,TlsFingerprintValidation'
unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
'MySQL 9.0':
image: 'mysql:9.0'
connectionStringExtra: 'AllowPublicKeyRetrieval=True'
unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime,Redirection,TlsFingerprintValidation'
unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
'MariaDB 10.6':
image: 'mariadb:10.6'
connectionStringExtra: ''
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin,Redirection,TlsFingerprintValidation'
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
'MariaDB 10.11':
image: 'mariadb:10.11'
connectionStringExtra: ''
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin,Redirection,TlsFingerprintValidation'
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
'MariaDB 11.4':
image: 'mariadb:11.4'
connectionStringExtra: ''
Expand Down
22 changes: 11 additions & 11 deletions src/MySqlConnector/Core/ConnectionPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@

namespace MySqlConnector.Core;

internal sealed class ConnectionPool : IDisposable
internal sealed class ConnectionPool : IConnectionPoolMetadata, IDisposable
{
public int Id { get; }

ConnectionPool? IConnectionPoolMetadata.ConnectionPool => this;

int IConnectionPoolMetadata.Generation => m_generation;

int IConnectionPoolMetadata.GetNewSessionId() => Interlocked.Increment(ref m_lastSessionId);

public string? Name { get; }

public ConnectionSettings ConnectionSettings { get; }
Expand Down Expand Up @@ -111,11 +117,8 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
}

// create a new session
session = await ServerSession.ConnectAndRedirectAsync(
() => new ServerSession(m_connectionLogger, this, m_generation,
Interlocked.Increment(ref m_lastSessionId)), m_logger, Id, ConnectionSettings, m_loadBalancer,
connection, s_createdNewSession, startingTimestamp, activity, ioBehavior, cancellationToken)
.ConfigureAwait(false);
session = await ServerSession.ConnectAndRedirectAsync(m_connectionLogger, m_logger, this, ConnectionSettings, m_loadBalancer,
connection, s_createdNewSession, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
AdjustHostConnectionCount(session, 1);
session.OwningConnection = new(connection);
int leasedSessionsCountNew;
Expand Down Expand Up @@ -411,11 +414,8 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh

try
{
var session = await ServerSession.ConnectAndRedirectAsync(
() => new ServerSession(m_connectionLogger, this, m_generation,
Interlocked.Increment(ref m_lastSessionId)), m_logger, Id, ConnectionSettings, m_loadBalancer,
connection, s_createdToReachMinimumPoolSize, Stopwatch.GetTimestamp(), null, ioBehavior,
cancellationToken).ConfigureAwait(false);
var session = await ServerSession.ConnectAndRedirectAsync(m_connectionLogger, m_logger, this, ConnectionSettings, m_loadBalancer,
connection, s_createdToReachMinimumPoolSize, Stopwatch.GetTimestamp(), null, ioBehavior, cancellationToken).ConfigureAwait(false);
AdjustHostConnectionCount(session, 1);
lock (m_sessions)
_ = m_sessions.AddFirst(session);
Expand Down
26 changes: 26 additions & 0 deletions src/MySqlConnector/Core/IConnectionPoolMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace MySqlConnector.Core;

internal interface IConnectionPoolMetadata
{
/// <summary>
/// Returns the <see cref="ConnectionPool"/> this <see cref="IConnectionPoolMetadata"/> is associated with,
/// or <c>null</c> if it represents a non-pooled connection.
/// </summary>
ConnectionPool? ConnectionPool { get; }

/// <summary>
/// Returns the ID of the connection pool, or 0 if this is a non-pooled connection.
/// </summary>
int Id { get; }

/// <summary>
/// Returns the generation of the connection pool, or 0 if this is a non-pooled connection.
/// </summary>
int Generation { get; }

/// <summary>
/// Returns a new session ID.
/// </summary>
/// <returns>A new session ID.</returns>
int GetNewSessionId();
}
13 changes: 13 additions & 0 deletions src/MySqlConnector/Core/NonPooledConnectionPoolMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace MySqlConnector.Core;

internal sealed class NonPooledConnectionPoolMetadata : IConnectionPoolMetadata
{
public static IConnectionPoolMetadata Instance { get; } = new NonPooledConnectionPoolMetadata();

public ConnectionPool? ConnectionPool => null;
public int Id => 0;
public int Generation => 0;
public int GetNewSessionId() => Interlocked.Increment(ref m_lastId);

private int m_lastId;
}
37 changes: 16 additions & 21 deletions src/MySqlConnector/Core/ServerSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,16 @@ namespace MySqlConnector.Core;

internal sealed partial class ServerSession : IServerCapabilities
{
public ServerSession(ILogger logger)
: this(logger, null, 0, Interlocked.Increment(ref s_lastId))
{
}

public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, int id)
public ServerSession(ILogger logger, IConnectionPoolMetadata pool)
{
m_logger = logger;
m_lock = new();
m_payloadCache = new();
Id = (pool?.Id ?? 0) + "." + id;
Id = pool.Id + "." + pool.GetNewSessionId();
ServerVersion = ServerVersion.Empty;
CreatedTimestamp = Stopwatch.GetTimestamp();
Pool = pool;
PoolGeneration = poolGeneration;
Pool = pool.ConnectionPool;
PoolGeneration = pool.Generation;
HostName = "";
m_activityTags = [];
DataReader = new();
Expand Down Expand Up @@ -665,10 +660,11 @@ private bool ValidateFingerPrint(byte[]? validationHash, ReadOnlySpan<byte> chal
return string.Equals(clientGeneratedHash, serverGeneratedHash, StringComparison.Ordinal);
}

public static async ValueTask<ServerSession> ConnectAndRedirectAsync(Func<ServerSession> createSession, ILogger logger, int? poolId, ConnectionSettings cs, ILoadBalancer? loadBalancer, MySqlConnection connection, Action<ILogger, int, string, Exception?>? logMessage, long startingTimestamp, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
public static async ValueTask<ServerSession> ConnectAndRedirectAsync(ILogger connectionLogger, ILogger poolLogger, IConnectionPoolMetadata pool, ConnectionSettings cs, ILoadBalancer? loadBalancer, MySqlConnection connection, Action<ILogger, int, string, Exception?>? logMessage, long startingTimestamp, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
{
var session = createSession();
if (poolId is not null && logger.IsEnabled(LogLevel.Debug)) logMessage!(logger, poolId.Value, session.Id, null);
var session = new ServerSession(connectionLogger, pool);
if (logMessage is not null && poolLogger.IsEnabled(LogLevel.Debug))
logMessage(poolLogger, pool.Id, session.Id, null);

string? redirectionUrl;
try
Expand All @@ -684,10 +680,10 @@ public static async ValueTask<ServerSession> ConnectAndRedirectAsync(Func<Server
Exception? redirectionException = null;
if (redirectionUrl is not null)
{
Log.HasServerRedirectionHeader(logger, session.Id, redirectionUrl);
Log.HasServerRedirectionHeader(connectionLogger, session.Id, redirectionUrl);
if (cs.ServerRedirectionMode == MySqlServerRedirectionMode.Disabled)
{
Log.ServerRedirectionIsDisabled(logger, session.Id);
Log.ServerRedirectionIsDisabled(connectionLogger, session.Id);
return session;
}

Expand All @@ -696,19 +692,19 @@ public static async ValueTask<ServerSession> ConnectAndRedirectAsync(Func<Server
if (host != cs.HostNames![0] || port != cs.Port || user != cs.UserID)
{
var redirectedSettings = cs.CloneWith(host, port, user);
Log.OpeningNewConnection(logger, host, port, user);
var redirectedSession = createSession();
Log.OpeningNewConnection(connectionLogger, session.Id, host, port, user);
var redirectedSession = new ServerSession(connectionLogger, pool);
try
{
await redirectedSession.ConnectAsync(redirectedSettings, connection, startingTimestamp, loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
Log.ClosingSessionToUseRedirectedSession(logger, session.Id, redirectedSession.Id);
Log.ClosingSessionToUseRedirectedSession(connectionLogger, session.Id, redirectedSession.Id);
await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
return redirectedSession;
}
catch (Exception ex)
{
redirectionException = ex;
Log.FailedToConnectRedirectedSession(logger, ex, redirectedSession.Id);
Log.FailedToConnectRedirectedSession(connectionLogger, ex, session.Id, redirectedSession.Id);
try
{
await redirectedSession.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
Expand All @@ -720,14 +716,14 @@ public static async ValueTask<ServerSession> ConnectAndRedirectAsync(Func<Server
}
else
{
Log.SessionAlreadyConnectedToServer(logger, session.Id);
Log.SessionAlreadyConnectedToServer(connectionLogger, session.Id);
}
}
}

if (cs.ServerRedirectionMode == MySqlServerRedirectionMode.Required)
{
Log.RequiresServerRedirection(logger, session.Id);
Log.RequiresServerRedirection(connectionLogger, session.Id);
throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support redirection", redirectionException);
}
return session;
Expand Down Expand Up @@ -2099,7 +2095,6 @@ protected override void OnStatementBegin(int index)
private static readonly PayloadData s_sleepWithAttributesPayload = QueryPayload.Create(true, "SELECT SLEEP(0) INTO @\uE001MySqlConnector\uE001Sleep;"u8);
private static readonly PayloadData s_selectConnectionIdVersionNoAttributesPayload = QueryPayload.Create(false, "SELECT CONNECTION_ID(), VERSION();"u8);
private static readonly PayloadData s_selectConnectionIdVersionWithAttributesPayload = QueryPayload.Create(true, "SELECT CONNECTION_ID(), VERSION();"u8);
private static int s_lastId;

private readonly ILogger m_logger;
private readonly object m_lock;
Expand Down
16 changes: 8 additions & 8 deletions src/MySqlConnector/Logging/Log.cs
Original file line number Diff line number Diff line change
Expand Up @@ -405,23 +405,23 @@ internal static partial class Log
[LoggerMessage(EventIds.HasServerRedirectionHeader, LogLevel.Trace, "Session {SessionId} has server redirection header {Header}")]
public static partial void HasServerRedirectionHeader(ILogger logger, string sessionId, string header);

[LoggerMessage(EventIds.ServerRedirectionIsDisabled, LogLevel.Trace, "Session {SessionId}, server redirection is disabled; ignoring redirection")]
[LoggerMessage(EventIds.ServerRedirectionIsDisabled, LogLevel.Trace, "Session {SessionId} server redirection is disabled; ignoring redirection")]
public static partial void ServerRedirectionIsDisabled(ILogger logger, string sessionId);

[LoggerMessage(EventIds.OpeningNewConnection, LogLevel.Debug, "opening new connection to {Host}:{Port} as {User}")]
public static partial void OpeningNewConnection(ILogger logger, string host, int port, string user);
[LoggerMessage(EventIds.OpeningNewConnection, LogLevel.Debug, "Session {SessionId} opening new connection to {Host}:{Port} as {User}")]
public static partial void OpeningNewConnection(ILogger logger, string sessionId, string host, int port, string user);

[LoggerMessage(EventIds.FailedToConnectRedirectedSession, LogLevel.Information, "failed to connect redirected session {SessionId}")]
public static partial void FailedToConnectRedirectedSession(ILogger logger, Exception ex, string sessionId);
[LoggerMessage(EventIds.FailedToConnectRedirectedSession, LogLevel.Information, "Session {SessionId} failed to connect redirected session {RedirectedSessionId}")]
public static partial void FailedToConnectRedirectedSession(ILogger logger, Exception ex, string sessionId, string redirectedSessionId);

[LoggerMessage(EventIds.ClosingSessionToUseRedirectedSession, LogLevel.Trace, "closing session {SessionId} to use redirected session {RedirectedSessionId} instead")]
[LoggerMessage(EventIds.ClosingSessionToUseRedirectedSession, LogLevel.Trace, "Closing session {SessionId} to use redirected session {RedirectedSessionId} instead")]
public static partial void ClosingSessionToUseRedirectedSession(ILogger logger, string sessionId, string redirectedSessionId);

[LoggerMessage(EventIds.SessionAlreadyConnectedToServer, LogLevel.Trace, "Session {SessionId} is already connected to this server; ignoring redirection")]
public static partial void SessionAlreadyConnectedToServer(ILogger logger, string sessionId);

[LoggerMessage(EventIds.RequiresServerRedirection, LogLevel.Error, "Session {SessionId}, new connection requires server redirection but server doesn't support it")]
public static partial void RequiresServerRedirection(ILogger logger, string SessionId);
[LoggerMessage(EventIds.RequiresServerRedirection, LogLevel.Error, "Session {SessionId} requires server redirection but server doesn't support it")]
public static partial void RequiresServerRedirection(ILogger logger, string sessionId);

[LoggerMessage(EventIds.CreatedPoolWillNotBeUsed, LogLevel.Debug, "Pool {PoolId} was created but will not be used (due to race)")]
public static partial void CreatedPoolWillNotBeUsed(ILogger logger, int poolId);
Expand Down
5 changes: 4 additions & 1 deletion src/MySqlConnector/MySqlConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#if NET6_0_OR_GREATER
using System.Globalization;
#endif
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
Expand Down Expand Up @@ -1064,7 +1065,7 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
// only "fail over" and "random" load balancers supported without connection pooling
var loadBalancer = connectionSettings.LoadBalance == MySqlLoadBalance.Random && connectionSettings.HostNames!.Count > 1 ?
RandomLoadBalancer.Instance : FailOverLoadBalancer.Instance;
var session = await ServerSession.ConnectAndRedirectAsync(() => new ServerSession(m_logger), m_logger, null, connectionSettings, loadBalancer, this, null, startingTimestamp, null, actualIOBehavior, cancellationToken).ConfigureAwait(false);
var session = await ServerSession.ConnectAndRedirectAsync(m_logger, m_logger, NonPooledConnectionPoolMetadata.Instance, connectionSettings, loadBalancer, this, null, startingTimestamp, null, actualIOBehavior, connectToken).ConfigureAwait(false);
session.OwningConnection = new WeakReference<MySqlConnection>(this);
Log.CreatedNonPooledSession(m_logger, session.Id);
return session;
Expand Down Expand Up @@ -1104,6 +1105,8 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,

internal SslProtocols SslProtocol => m_session!.SslProtocol;

internal IPEndPoint? SessionEndPoint => m_session!.IPEndPoint;

internal void SetState(ConnectionState newState)
{
if (m_connectionState != newState)
Expand Down
Loading

0 comments on commit 098bd5b

Please sign in to comment.