-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Added PoolBlockingPeriod connection property #29697
Changes from 5 commits
6d9fe19
b944ac6
d906f44
9060418
a68899e
5777789
47ac554
c16c892
77fd936
7e76fd8
9590a9f
83f124e
563cb99
3d592cd
ec47490
24bb30b
edae126
92c30d9
3551f7b
286eb8f
365505a
880e4f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -98,6 +98,142 @@ internal static string ConvertToString(object value) | |
throw ADP.ConvertFailed(value.GetType(), typeof(String), e); | ||
} | ||
} | ||
#region <<PoolBlockingPeriod Utility>> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: space above this region line |
||
const string PoolBlockingPeriodAutoString = "Auto"; | ||
const string PoolBlockingPeriodAlwaysBlockString = "AlwaysBlock"; | ||
const string PoolBlockingPeriodNeverBlockString = "NeverBlock"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than naming these consts like this, could you just use |
||
|
||
internal static bool TryConvertToPoolBlockingPeriod(string value, out PoolBlockingPeriod result) | ||
{ | ||
Debug.Assert(Enum.GetNames(typeof(PoolBlockingPeriod)).Length == 3, "PoolBlockingPeriod enum has changed, update needed"); | ||
Debug.Assert(null != value, "TryConvertToPoolBlockingPeriod(null,...)"); | ||
|
||
if (StringComparer.OrdinalIgnoreCase.Equals(value, PoolBlockingPeriodAutoString)) | ||
{ | ||
result = PoolBlockingPeriod.Auto; | ||
return true; | ||
} | ||
else if (StringComparer.OrdinalIgnoreCase.Equals(value, PoolBlockingPeriodAlwaysBlockString)) | ||
{ | ||
result = PoolBlockingPeriod.AlwaysBlock; | ||
return true; | ||
} | ||
else if (StringComparer.OrdinalIgnoreCase.Equals(value, PoolBlockingPeriodNeverBlockString)) | ||
{ | ||
result = PoolBlockingPeriod.NeverBlock; | ||
return true; | ||
} | ||
else | ||
{ | ||
result = DbConnectionStringDefaults.PoolBlockingPeriod; | ||
return false; | ||
} | ||
} | ||
|
||
internal static bool IsValidPoolBlockingPeriodValue(PoolBlockingPeriod value) | ||
{ | ||
Debug.Assert(Enum.GetNames(typeof(PoolBlockingPeriod)).Length == 3, "PoolBlockingPeriod enum has changed, update needed"); | ||
return value == PoolBlockingPeriod.Auto || value == PoolBlockingPeriod.AlwaysBlock || value == PoolBlockingPeriod.NeverBlock; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What you have is fine, but this could also just be: return (uint)value <= (uint)PoolBlockingPeriod.NeverBlock; |
||
} | ||
|
||
internal static string PoolBlockingPeriodToString(PoolBlockingPeriod value) | ||
{ | ||
Debug.Assert(IsValidPoolBlockingPeriodValue(value)); | ||
|
||
if (value == PoolBlockingPeriod.AlwaysBlock) | ||
{ | ||
return PoolBlockingPeriodAlwaysBlockString; | ||
} | ||
if (value == PoolBlockingPeriod.NeverBlock) | ||
{ | ||
return PoolBlockingPeriodNeverBlockString; | ||
} | ||
else | ||
{ | ||
return PoolBlockingPeriodAutoString; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, I think this would be cleaner as a switch: switch (value)
{
case PoolBlockingPeriod.AlwaysBlock: return nameof(PoolBlockingPeriod.AlwaysBlock);
case PoolBlockingPeriod.NeverBlock: return nameof(PoolBlockingPeriod.NeverBlock);
default: return nameof(PoolBlockingPeriod.Auto);
} |
||
} | ||
|
||
/// <summary> | ||
/// This method attempts to convert the given value to a PoolBlockingPeriod enum. The algorithm is: | ||
/// * if the value is from type string, it will be matched against PoolBlockingPeriod enum names only, using ordinal, case-insensitive comparer | ||
/// * if the value is from type PoolBlockingPeriod, it will be used as is | ||
/// * if the value is from integral type (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, or UInt64), it will be converted to enum | ||
/// * if the value is another enum or any other type, it will be blocked with an appropriate ArgumentException | ||
/// | ||
/// in any case above, if the conerted value is out of valid range, the method raises ArgumentOutOfRangeException. | ||
/// </summary> | ||
/// <returns>PoolBlockingPeriod value in the valid range</returns> | ||
internal static PoolBlockingPeriod ConvertToPoolBlockingPeriod(string keyword, object value) | ||
{ | ||
Debug.Assert(null != value, "ConvertToPoolBlockingPeriod(null)"); | ||
string sValue = (value as string); | ||
PoolBlockingPeriod result; | ||
if (null != sValue) | ||
{ | ||
// We could use Enum.TryParse<PoolBlockingPeriod> here, but it accepts value combinations like | ||
// "ReadOnly, ReadWrite" which are unwelcome here | ||
// Also, Enum.TryParse is 100x slower than plain StringComparer.OrdinalIgnoreCase.Equals method. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this used on a hot path? |
||
|
||
if (TryConvertToPoolBlockingPeriod(sValue, out result)) | ||
{ | ||
return result; | ||
} | ||
|
||
// try again after remove leading & trailing whitespaces. | ||
sValue = sValue.Trim(); | ||
if (TryConvertToPoolBlockingPeriod(sValue, out result)) | ||
{ | ||
return result; | ||
} | ||
|
||
// string values must be valid | ||
throw ADP.InvalidConnectionOptionValue(keyword); | ||
} | ||
else | ||
{ | ||
// the value is not string, try other options | ||
PoolBlockingPeriod eValue; | ||
|
||
if (value is PoolBlockingPeriod) | ||
{ | ||
// quick path for the most common case | ||
eValue = (PoolBlockingPeriod)value; | ||
} | ||
else if (value.GetType().IsEnum) | ||
{ | ||
// explicitly block scenarios in which user tries to use wrong enum types, like: | ||
// builder["PoolBlockingPeriod"] = EnvironmentVariableTarget.Process; | ||
// workaround: explicitly cast non-PoolBlockingPeriod enums to int | ||
throw ADP.ConvertFailed(value.GetType(), typeof(PoolBlockingPeriod), null); | ||
} | ||
else | ||
{ | ||
try | ||
{ | ||
// Enum.ToObject allows only integral and enum values (enums are blocked above), rasing ArgumentException for the rest | ||
eValue = (PoolBlockingPeriod)Enum.ToObject(typeof(PoolBlockingPeriod), value); | ||
} | ||
catch (ArgumentException e) | ||
{ | ||
// to be consistent with the messages we send in case of wrong type usage, replace | ||
// the error with our exception, and keep the original one as inner one for troubleshooting | ||
throw ADP.ConvertFailed(value.GetType(), typeof(PoolBlockingPeriod), e); | ||
} | ||
} | ||
|
||
// ensure value is in valid range | ||
if (IsValidPoolBlockingPeriodValue(eValue)) | ||
{ | ||
return eValue; | ||
} | ||
else | ||
{ | ||
throw ADP.InvalidEnumerationValue(typeof(ApplicationIntent), (int)eValue); | ||
} | ||
} | ||
} | ||
#endregion | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So far all of the code has been about converting to/from this enum. I'm surprised all of that code is needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you elaborate more on this? There is a similar method |
||
|
||
private const string ApplicationIntentReadWriteString = "ReadWrite"; | ||
private const string ApplicationIntentReadOnlyString = "ReadOnly"; | ||
|
@@ -259,6 +395,7 @@ internal static partial class DbConnectionStringDefaults | |
internal const string TransactionBinding = "Implicit Unbind"; | ||
internal const int ConnectRetryCount = 1; | ||
internal const int ConnectRetryInterval = 10; | ||
internal const PoolBlockingPeriod PoolBlockingPeriod = SqlClient.PoolBlockingPeriod.Auto; | ||
} | ||
|
||
|
||
|
@@ -306,6 +443,7 @@ internal static partial class DbConnectionStringKeywords | |
internal const string MaxPoolSize = "Max Pool Size"; | ||
internal const string Pooling = "Pooling"; | ||
internal const string MinPoolSize = "Min Pool Size"; | ||
internal const string PoolBlockingPeriod = "PoolBlockingPeriod"; | ||
} | ||
|
||
internal static class DbConnectionStringSynonyms | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
|
||
using System.Collections.Generic; | ||
using System.Data.Common; | ||
using System.Data.SqlClient; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can get rid of this |
||
using System.Diagnostics; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
@@ -668,6 +669,45 @@ private Timer CreateCleanupTimer() => | |
_cleanupWait, | ||
_cleanupWait); | ||
|
||
private bool IsBlockingPeriodEnabled() | ||
{ | ||
var poolGroupConnectionOptions = _connectionPoolGroup.ConnectionOptions as SqlConnectionString; | ||
if (poolGroupConnectionOptions == null) | ||
{ | ||
return true; | ||
} | ||
var policy = poolGroupConnectionOptions.PoolBlockingPeriod; | ||
|
||
switch (policy) | ||
{ | ||
case System.Data.SqlClient.PoolBlockingPeriod.Auto: | ||
{ | ||
if (ADP.IsAzureSqlServerEndpoint(poolGroupConnectionOptions.DataSource)) | ||
{ | ||
return false; // in Azure it will be Disabled | ||
} | ||
else | ||
{ | ||
return true; // in Non Azure, it will be Enabled | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: return !ADP.IsAzureSqlServerEndpoint(poolGroupConnectionOptions.DataSource); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
} | ||
case System.Data.SqlClient.PoolBlockingPeriod.AlwaysBlock: | ||
{ | ||
return true; //Enabled | ||
} | ||
case System.Data.SqlClient.PoolBlockingPeriod.NeverBlock: | ||
{ | ||
return false; //Disabled | ||
} | ||
default: | ||
{ | ||
//we should never get into this path. | ||
Debug.Fail("Unknown PoolBlockingPeriod. Please specify explicit results in above switch case statement."); | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) | ||
{ | ||
DbConnectionInternal newObj = null; | ||
|
@@ -719,6 +759,10 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio | |
{ | ||
throw; | ||
} | ||
if (!IsBlockingPeriodEnabled()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am OK with this approach. However an alternative could have been to call a function here internal void CheckPoolBlockingPeriod()
{
if (!IsBlockingPeriodEnabled())
{
throw;
}
} |
||
{ | ||
throw; | ||
} | ||
newObj = null; // set to null, so we do not return bad new object | ||
// Failed to create instance | ||
_resError = e; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
|
||
namespace System.Data.SqlClient | ||
{ | ||
public enum PoolBlockingPeriod | ||
{ | ||
Auto = 0, // Blocking period OFF for Azure SQL servers, but ON for all other SQL servers. | ||
AlwaysBlock = 1, // Blocking period ON for all SQL servers including Azure SQL servers. | ||
NeverBlock = 2, // Blocking period OFF for all SQL servers including Azure SQL servers. | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this deleted ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure why this got deleted. These changes appeared as I ran "msbuild /t:UpdatePackageIndex" command. Looking forward to @weshaggard for more guidance on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect to see the SqlClient version bumped in this file. I'm not entirely sure why these 2 entries were removed but I suspect it was because they were redundant. @ericstj @joperezr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, they are redundant. The actual data model should not write them but someone manually editing would. Is that what you did here @weshaggard? 2414032#diff-122916076db7087dbc454352fada61eeR2447
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a couple iterations at one point I generated them with the package index update task but I may have manually changed them later for these too. At any rate @afsanehr these changes are safe but I still don't see the SqlClient version updated like I would expect. I would expect to see https://github.com/AfsanehR/corefx/blob/577778967bf657d325bd999d93dc62458e2dc0a8/pkg/Microsoft.Private.PackageBaseline/packageIndex.json#L1254 updated to the new version. Once you build the library with the new version can you again try to run the UpdatePackageIndex target on the pkgproj for it SqlClient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@weshaggard Running build -allConfigurations, I am getting this error:
D:\j\workspace\windows-TGrou---2a8f9c29\Tools\partialfacades.task.targets(64,5): error : Errors were encountered when generating facade(s). [D:\j\workspace\windows-TGrou---2a8f9c29\src\System.Data.SqlClient\src\System.Data.SqlClient.csproj]
Do you have any suggestion how to fix this?