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

Draft | Addressing name pipe failures on sync and async calls. #1880

Closed
wants to merge 10 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal sealed class SNINpHandle : SNIPhysicalHandle
private int _bufferSize = TdsEnums.DEFAULT_LOGIN_PACKET_SIZE;
private readonly Guid _connectionId = Guid.NewGuid();

public SNINpHandle(string serverName, string pipeName, long timerExpire, bool tlsFirst)
public SNINpHandle(string serverName, string pipeName, long timerExpire, bool tlsFirst, bool isAsyncOption)
{
using (TrySNIEventScope.Create(nameof(SNINpHandle)))
{
Expand All @@ -48,11 +48,13 @@ public SNINpHandle(string serverName, string pipeName, long timerExpire, bool tl
_tlsFirst = tlsFirst;
try
{
PipeOptions pipeOptions = isAsyncOption ? PipeOptions.Asynchronous | PipeOptions.WriteThrough : PipeOptions.WriteThrough;

_pipeStream = new NamedPipeClientStream(
serverName,
pipeName,
PipeDirection.InOut,
PipeOptions.Asynchronous | PipeOptions.WriteThrough);
pipeOptions);

bool isInfiniteTimeOut = long.MaxValue == timerExpire;
if (isInfiniteTimeOut)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ internal static SNIHandle CreateConnectionHandle(
tlsFirst, hostNameInCertificate, serverCertificateFilename);
break;
case DataSource.Protocol.NP:
sniHandle = CreateNpHandle(details, timerExpire, parallel, tlsFirst);
sniHandle = CreateNpHandle(details, timerExpire, parallel, tlsFirst, isAsyncOption : async);
break;
default:
Debug.Fail($"Unexpected connection protocol: {details._connectionProtocol}");
Expand Down Expand Up @@ -348,16 +348,17 @@ private static SNITCPHandle CreateTcpHandle(
/// <param name="timerExpire">Timer expiration</param>
/// <param name="parallel">Should MultiSubnetFailover be used. Only returns an error for named pipes.</param>
/// <param name="tlsFirst"></param>
/// <param name="isAsyncOption"></param>
/// <returns>SNINpHandle</returns>
private static SNINpHandle CreateNpHandle(DataSource details, long timerExpire, bool parallel, bool tlsFirst)
private static SNINpHandle CreateNpHandle(DataSource details, long timerExpire, bool parallel, bool tlsFirst, bool isAsyncOption)
{
if (parallel)
{
// Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol
SNICommon.ReportSNIError(SNIProviders.NP_PROV, 0, SNICommon.MultiSubnetFailoverWithNonTcpProtocol, Strings.SNI_ERROR_49);
return null;
}
return new SNINpHandle(details.PipeHostName, details.PipeName, timerExpire, tlsFirst);
return new SNINpHandle(details.PipeHostName, details.PipeName, timerExpire, tlsFirst, isAsyncOption);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ private enum CultureCheckState : uint
}

private bool _AsyncCommandInProgress;
private bool _isAsyncPipeOption = false;

// SQLStatistics support
internal SqlStatistics _statistics;
Expand Down Expand Up @@ -496,6 +497,11 @@ internal bool AsyncCommandInProgress
set => _AsyncCommandInProgress = value;
}

internal bool IsAsyncPipeOption
{
get => _isAsyncPipeOption;
}

private bool UsesActiveDirectoryIntegrated(SqlConnectionString opt)
{
return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated;
Expand Down Expand Up @@ -1589,6 +1595,8 @@ private Task InternalOpenAsync(CancellationToken cancellationToken)
{
long scopeID = SqlClientEventSource.Log.TryPoolerScopeEnterEvent("SqlConnection.InternalOpenAsync | API | Object Id {0}", ObjectID);
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlConnection.InternalOpenAsync | API | Correlation | Object Id {0}, Activity Id {1}", ObjectID, ActivityCorrelator.Current);
_isAsyncPipeOption = true;

try
{
Guid operationId = s_diagnosticListener.WriteConnectionOpenBefore(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1911,7 +1911,8 @@ private void AttemptOneLogin(
ignoreSniOpenTimeout,
timeout.LegacyTimerExpire,
ConnectionOptions,
withFailover);
withFailover,
Connection.IsAsyncPipeOption);

_timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake);
_timeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.LoginBegin);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,8 @@ internal void Connect(
bool ignoreSniOpenTimeout,
long timerExpire,
SqlConnectionString connectionOptions,
bool withFailover)
bool withFailover,
bool isAsyncPipeOption)
{
SqlConnectionEncryptOption encrypt = connectionOptions.Encrypt;
bool isTlsFirst = (encrypt == SqlConnectionEncryptOption.Strict);
Expand Down Expand Up @@ -443,14 +444,15 @@ internal void Connect(
_connHandler.pendingSQLDNSObject = null;

// AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server

_physicalStateObj.CreatePhysicalSNIHandle(
serverInfo.ExtendedServerName,
ignoreSniOpenTimeout,
timerExpire,
out instanceName,
ref _sniSpnBuffer,
false,
true,
async: isAsyncPipeOption,
fParallel,
_connHandler.ConnectionOptions.IPAddressPreference,
FQDNforDNSCache,
Expand Down Expand Up @@ -545,10 +547,11 @@ internal void Connect(
_physicalStateObj.CreatePhysicalSNIHandle(
serverInfo.ExtendedServerName,
ignoreSniOpenTimeout,
timerExpire, out instanceName,
timerExpire,
out instanceName,
ref _sniSpnBuffer,
true,
true,
async: isAsyncPipeOption,
fParallel,
_connHandler.ConnectionOptions.IPAddressPreference,
FQDNforDNSCache,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,41 @@ public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider prov
Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution);
}

#if NETCOREAPP

[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
[MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)]
public async Task ConcurrentExecutionAsync(string cnnString, SqlRetryLogicBaseProvider provider)
{
int numberOfTries = provider.RetryLogic.NumberOfTries;
int cancelAfterRetries = numberOfTries + 1;
int retriesCount = 0;
int concurrentExecution = 5;
provider.Retrying += (s, e) => Interlocked.Increment(ref retriesCount);

await Parallel.ForEachAsync(System.Linq.Enumerable.Range(0, concurrentExecution),
async (i, c) =>
{
using (var cnn = CreateConnectionWithInvalidCatalog(cnnString, provider, cancelAfterRetries))
{
await Assert.ThrowsAsync<AggregateException>(async () => await cnn.OpenAsync());
}
});
Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution);

retriesCount = 0;
Parallel.For(0, concurrentExecution,
i =>
{
using (var cnn = CreateConnectionWithInvalidCatalog(cnnString, provider, cancelAfterRetries))
{
Assert.ThrowsAsync<AggregateException>(() => cnn.OpenAsync()).Wait();
}
});
Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution);
}
#endif

[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
[MemberData(nameof(RetryLogicTestHelper.GetNoneRetriableCondition), MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)]
public void DefaultOpenWithoutRetry(string connectionString, SqlRetryLogicBaseProvider cnnProvider)
Expand Down