From 69a202cea16fcea629425c56ed99ab8ae6a53826 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Tue, 19 Aug 2025 18:42:54 -0700 Subject: [PATCH 01/20] Add connection property for connection limit --- Snowflake.Data/Core/HttpUtil.cs | 14 ++++++++++---- .../Session/SFSessionHttpClientProperties.cs | 16 +++++++++++++++- Snowflake.Data/Core/Session/SFSessionProperty.cs | 2 ++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Snowflake.Data/Core/HttpUtil.cs b/Snowflake.Data/Core/HttpUtil.cs index de0e118a9..6c5b827ee 100755 --- a/Snowflake.Data/Core/HttpUtil.cs +++ b/Snowflake.Data/Core/HttpUtil.cs @@ -26,6 +26,7 @@ public HttpClientConfig( bool disableRetry, bool forceRetryOn404, int maxHttpRetries, + int connectionLimit, bool includeRetryReason = true) { CrlCheckEnabled = crlCheckEnabled; @@ -38,6 +39,7 @@ public HttpClientConfig( ForceRetryOn404 = forceRetryOn404; MaxHttpRetries = maxHttpRetries; IncludeRetryReason = includeRetryReason; + ConnectionLimit = connectionLimit; ConfKey = string.Join(";", new string[] { @@ -50,7 +52,8 @@ public HttpClientConfig( disableRetry.ToString(), forceRetryOn404.ToString(), maxHttpRetries.ToString(), - includeRetryReason.ToString()}); + includeRetryReason.ToString(), + connectionLimit.ToString()}); } public readonly bool CrlCheckEnabled; @@ -63,6 +66,7 @@ public HttpClientConfig( public readonly bool ForceRetryOn404; public readonly int MaxHttpRetries; public readonly bool IncludeRetryReason; + public readonly int ConnectionLimit; // Key used to identify the HttpClient with the configuration matching the settings public readonly string ConfKey; @@ -113,7 +117,7 @@ private HttpClient RegisterNewHttpClientIfNecessary(HttpClientConfig config, Del logger.Debug("Http client not registered. Adding."); var httpClient = new HttpClient( - new RetryHandler(SetupCustomHttpHandler(config, customHandler), config.DisableRetry, config.ForceRetryOn404, config.MaxHttpRetries, config.IncludeRetryReason)) + new RetryHandler(SetupCustomHttpHandler(config, customHandler), config.DisableRetry, config.ForceRetryOn404, config.MaxHttpRetries, config.IncludeRetryReason, config.ConnectionLimit)) { Timeout = Timeout.InfiniteTimeSpan }; @@ -335,13 +339,15 @@ private class RetryHandler : DelegatingHandler private bool forceRetryOn404; private int maxRetryCount; private bool includeRetryReason; + private int connectionLimit; - internal RetryHandler(HttpMessageHandler innerHandler, bool disableRetry, bool forceRetryOn404, int maxRetryCount, bool includeRetryReason) : base(innerHandler) + internal RetryHandler(HttpMessageHandler innerHandler, bool disableRetry, bool forceRetryOn404, int maxRetryCount, bool includeRetryReason, int connectionLimit) : base(innerHandler) { this.disableRetry = disableRetry; this.forceRetryOn404 = forceRetryOn404; this.maxRetryCount = maxRetryCount; this.includeRetryReason = includeRetryReason; + this.connectionLimit = connectionLimit; } protected override async Task SendAsync(HttpRequestMessage requestMessage, @@ -358,7 +364,7 @@ protected override async Task SendAsync(HttpRequestMessage ServicePoint p = ServicePointManager.FindServicePoint(requestMessage.RequestUri); p.Expect100Continue = false; // Saves about 100 ms per request p.UseNagleAlgorithm = false; // Saves about 200 ms per request - p.ConnectionLimit = 20; // Default value is 2, we need more connections for performing multiple parallel queries + p.ConnectionLimit = connectionLimit; // Default value is 2, we need more connections for performing multiple parallel queries TimeSpan httpTimeout = (TimeSpan)requestMessage.Properties[BaseRestRequest.HTTP_REQUEST_TIMEOUT_KEY]; TimeSpan restTimeout = (TimeSpan)requestMessage.Properties[BaseRestRequest.REST_REQUEST_TIMEOUT_KEY]; diff --git a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs index 7f8bd4cec..650ffa07d 100644 --- a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs +++ b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs @@ -20,6 +20,7 @@ internal class SFSessionHttpClientProperties public static readonly TimeSpan DefaultExpirationTimeout = TimeSpan.FromHours(1); public const bool DefaultPoolingEnabled = true; public const int DefaultMaxHttpRetries = 7; + public const int DefaultConnectionLimit = 20; public static readonly TimeSpan DefaultRetryTimeout = TimeSpan.FromSeconds(300); private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger(); @@ -41,6 +42,7 @@ internal class SFSessionHttpClientProperties private TimeSpan _expirationTimeout; private bool _poolingEnabled; internal bool _clientStoreTemporaryCredential; + internal int _connectionLimit; public static SFSessionHttpClientProperties ExtractAndValidate(SFSessionProperties properties) { @@ -92,6 +94,7 @@ private void CheckPropertiesAreValid() ValidateHttpRetries(); ValidateMinMaxPoolSize(); ValidateWaitingForSessionIdleTimeout(); + ValidateConnectionLimit(); } catch (SnowflakeDbException) { @@ -181,6 +184,15 @@ private void ValidateWaitingForSessionIdleTimeout() } } + private void ValidateConnectionLimit() + { + if (_connectionLimit < 1) + { + s_logger.Warn($"Connection limit cannot be less than 1. Using the default value of {DefaultConnectionLimit}"); + _connectionLimit = DefaultConnectionLimit; + } + } + public HttpClientConfig BuildHttpClientConfig() { return new HttpClientConfig( @@ -193,6 +205,7 @@ public HttpClientConfig BuildHttpClientConfig() disableRetry, forceRetryOn404, maxHttpRetries, + _connectionLimit, includeRetryReason); } @@ -253,7 +266,8 @@ public SFSessionHttpClientProperties ExtractProperties(SFSessionProperties prope _expirationTimeout = extractor.ExtractTimeout(SFSessionProperty.EXPIRATIONTIMEOUT), _poolingEnabled = extractor.ExtractBooleanWithDefaultValue(SFSessionProperty.POOLINGENABLED), _disableSamlUrlCheck = extractor.ExtractBooleanWithDefaultValue(SFSessionProperty.DISABLE_SAML_URL_CHECK), - _clientStoreTemporaryCredential = Boolean.Parse(propertiesDictionary[SFSessionProperty.CLIENT_STORE_TEMPORARY_CREDENTIAL]) + _clientStoreTemporaryCredential = Boolean.Parse(propertiesDictionary[SFSessionProperty.CLIENT_STORE_TEMPORARY_CREDENTIAL]), + _connectionLimit = int.Parse(propertiesDictionary[SFSessionProperty.CONNECTION_LIMIT]), }; } diff --git a/Snowflake.Data/Core/Session/SFSessionProperty.cs b/Snowflake.Data/Core/Session/SFSessionProperty.cs index 4cd4107ec..1b5bc1183 100644 --- a/Snowflake.Data/Core/Session/SFSessionProperty.cs +++ b/Snowflake.Data/Core/Session/SFSessionProperty.cs @@ -133,6 +133,8 @@ internal enum SFSessionProperty WIFENTRARESOURCE, [SFSessionPropertyAttr(required = false, defaultValue = "false")] OAUTHENABLESINGLEUSEREFRESHTOKENS, + [SFSessionPropertyAttr(required = false, defaultValue = "20")] + CONNECTION_LIMIT, } class SFSessionPropertyAttr : Attribute From a33320b5c8893c673f3f939a127e1952ebc3da6a Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Tue, 19 Aug 2025 18:43:22 -0700 Subject: [PATCH 02/20] Add tests for new connection limit property --- .../UnitTests/HttpUtilTest.cs | 6 ++-- .../Session/SFHttpClientPropertiesTest.cs | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/HttpUtilTest.cs b/Snowflake.Data.Tests/UnitTests/HttpUtilTest.cs index 33ac65b1e..4d4d94cbd 100644 --- a/Snowflake.Data.Tests/UnitTests/HttpUtilTest.cs +++ b/Snowflake.Data.Tests/UnitTests/HttpUtilTest.cs @@ -36,7 +36,7 @@ public async Task TestNonRetryableHttpExceptionThrowsError() .ThrowsAsync(new HttpRequestException("", new AuthenticationException())); var httpClient = HttpUtil.Instance.GetHttpClient( - new HttpClientConfig(false, "fakeHost", "fakePort", "user", "password", "fakeProxyList", false, false, 7), + new HttpClientConfig(false, "fakeHost", "fakePort", "user", "password", "fakeProxyList", false, false, 7, 20), handler.Object); try @@ -152,7 +152,8 @@ public void ShouldCreateHttpClientHandlerWithProxy() "localhost", false, false, - 7 + 7, + 20 ); // when @@ -176,6 +177,7 @@ public void ShouldCreateHttpClientHandlerWithoutProxy() null, false, false, + 20, 0 ); diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs index f129c77bc..37fead794 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs @@ -50,6 +50,37 @@ public void TestConvertToMapOnly2Properties( Assert.AreEqual(clientStoreTemporaryCredential, parameterMap[SFSessionParameter.CLIENT_STORE_TEMPORARY_CREDENTIAL]); } + [Test] + [TestCase(1)] + [TestCase(10)] + [TestCase(100)] + public void TestSettingConnectionLimitProperty(int expectedConnectionLimit) + { + // arrange + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;CONNECTION_LIMIT={expectedConnectionLimit}"; + var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); + + // act + var extractedProperties = SFSessionHttpClientProperties.ExtractAndValidate(properties); + + // assert + Assert.AreEqual(expectedConnectionLimit, extractedProperties._connectionLimit); + } + + [Test] + public void TestSettingConnectionLimitPropertyToLessThan1() + { + // arrange + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;CONNECTION_LIMIT={0}"; + var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); + + // act + var extractedProperties = SFSessionHttpClientProperties.ExtractAndValidate(properties); + + // assert + Assert.AreEqual(SFSessionHttpClientProperties.DefaultConnectionLimit, extractedProperties._connectionLimit); + } + [Test] public void TestBuildHttpClientConfig() { From 51a57ba40d78884d8bbf47dd74d505f42d50e357 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Tue, 2 Sep 2025 10:56:53 -0700 Subject: [PATCH 03/20] Add value for connection limit parameter --- Snowflake.Data.Tests/IntegrationTests/CertificateRevocationIT.cs | 1 + .../UnitTests/Revocation/CertificateRevocationVerifierTest.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Snowflake.Data.Tests/IntegrationTests/CertificateRevocationIT.cs b/Snowflake.Data.Tests/IntegrationTests/CertificateRevocationIT.cs index 7a4315141..d3a709d9c 100644 --- a/Snowflake.Data.Tests/IntegrationTests/CertificateRevocationIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/CertificateRevocationIT.cs @@ -27,6 +27,7 @@ public void TestCertificate() false, false, 3, + 20, true, false, CertRevocationCheckMode.Enabled.ToString(), diff --git a/Snowflake.Data.Tests/UnitTests/Revocation/CertificateRevocationVerifierTest.cs b/Snowflake.Data.Tests/UnitTests/Revocation/CertificateRevocationVerifierTest.cs index c514f1ac3..f6cef2fd0 100644 --- a/Snowflake.Data.Tests/UnitTests/Revocation/CertificateRevocationVerifierTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Revocation/CertificateRevocationVerifierTest.cs @@ -144,6 +144,7 @@ private HttpClientConfig GetHttpConfig() => false, false, 3, + 20, true, false, CertRevocationCheckMode.Enabled.ToString(), From 0129b2d6413c01b6d8effb43cdeaaf81588cf6a2 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Wed, 3 Sep 2025 10:14:28 -0700 Subject: [PATCH 04/20] Update docs --- doc/Connecting.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/Connecting.md b/doc/Connecting.md index d67fcd194..f273ce573 100644 --- a/doc/Connecting.md +++ b/doc/Connecting.md @@ -62,6 +62,7 @@ The following table lists all valid connection properties: | WORKLOAD_IDENTITY_PROVIDER | Depends | The type of attestation provider for Workload Identity Federation authentication. You can specify one of following values: `OIDC`, `AZURE`, `AWS`, `GCP`. It is mandatory when Authenticator is `workload_identity`. | | WORKLOAD_IDENTITY_ENTRA_RESOURCE | No | The entra resource used for Azure provider in Workload Identity Federation authentication. The default value for it is `api://fd3f753b-eed3-462c-b6a7-a4b5bb650aad`. | | OAUTHENABLESINGLEUSEREFRESHTOKENS | No | Used in OAuth Authorization Code Flow authentication. The default value is `false`. When set to `true` the driver requests the Identity Provider for single use refresh tokens. | +| CONNECTION_LIMIT | No | The maximum amount of connections allowed for the ServicePoint object. The default value is 20. |
**Note**: Connections should not be shared across multiple threads. From 9c8378c690c646e5ff7438783f4bd5c42a2cd023 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 5 Sep 2025 10:15:23 -0700 Subject: [PATCH 05/20] Trigger build checks From 958411b4cb980c9f0d4a8bdbce9ecc2ae3857b82 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 5 Sep 2025 11:35:03 -0700 Subject: [PATCH 06/20] Trigger build checks From 046d9647c00864d6f43a886f075d154c7fab7758 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 12 Sep 2025 10:32:41 -0700 Subject: [PATCH 07/20] Add tests for non string values --- .../Session/SFHttpClientPropertiesTest.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs index 37fead794..d22915a43 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs @@ -81,6 +81,33 @@ public void TestSettingConnectionLimitPropertyToLessThan1() Assert.AreEqual(SFSessionHttpClientProperties.DefaultConnectionLimit, extractedProperties._connectionLimit); } + [Test] + public void TestSettingConnectionLimitPropertyToNoValue() + { + // arrange + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;CONNECTION_LIMIT="; + var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); + + // act + var extractedProperties = SFSessionHttpClientProperties.ExtractAndValidate(properties); + + // assert + Assert.AreEqual(SFSessionHttpClientProperties.DefaultConnectionLimit, extractedProperties._connectionLimit); + } + + [Test] + public void TestThrowsExceptionWhenSettingConnectionLimitPropertyToNonStringValue() + { + // arrange + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;CONNECTION_LIMIT=abc"; + + // act + var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); + + // assert + var thrown = Assert.Throws(() => SFSessionHttpClientProperties.ExtractAndValidate(properties)); + } + [Test] public void TestBuildHttpClientConfig() { From fb095f3b61ab55f1eaa32aedfa582a6b9e187828 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 12 Sep 2025 10:49:05 -0700 Subject: [PATCH 08/20] Rename new property to SERVICE_POINT_CONNECTION_LIMIT --- .../UnitTests/Session/SFHttpClientPropertiesTest.cs | 12 ++++++------ .../Core/Session/SFSessionHttpClientProperties.cs | 10 +++++----- Snowflake.Data/Core/Session/SFSessionProperty.cs | 2 +- doc/Connecting.md | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs index d22915a43..79237ced4 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs @@ -64,42 +64,42 @@ public void TestSettingConnectionLimitProperty(int expectedConnectionLimit) var extractedProperties = SFSessionHttpClientProperties.ExtractAndValidate(properties); // assert - Assert.AreEqual(expectedConnectionLimit, extractedProperties._connectionLimit); + Assert.AreEqual(expectedConnectionLimit, extractedProperties._servicePointConnectionLimit); } [Test] public void TestSettingConnectionLimitPropertyToLessThan1() { // arrange - var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;CONNECTION_LIMIT={0}"; + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;SERVICE_POINT_CONNECTION_LIMIT={0}"; var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); // act var extractedProperties = SFSessionHttpClientProperties.ExtractAndValidate(properties); // assert - Assert.AreEqual(SFSessionHttpClientProperties.DefaultConnectionLimit, extractedProperties._connectionLimit); + Assert.AreEqual(SFSessionHttpClientProperties.DefaultConnectionLimit, extractedProperties._servicePointConnectionLimit); } [Test] public void TestSettingConnectionLimitPropertyToNoValue() { // arrange - var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;CONNECTION_LIMIT="; + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;SERVICE_POINT_CONNECTION_LIMIT="; var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); // act var extractedProperties = SFSessionHttpClientProperties.ExtractAndValidate(properties); // assert - Assert.AreEqual(SFSessionHttpClientProperties.DefaultConnectionLimit, extractedProperties._connectionLimit); + Assert.AreEqual(SFSessionHttpClientProperties.DefaultConnectionLimit, extractedProperties._servicePointConnectionLimit); } [Test] public void TestThrowsExceptionWhenSettingConnectionLimitPropertyToNonStringValue() { // arrange - var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;CONNECTION_LIMIT=abc"; + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;SERVICE_POINT_CONNECTION_LIMIT=abc"; // act var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); diff --git a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs index 650ffa07d..70a1f59a6 100644 --- a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs +++ b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs @@ -42,7 +42,7 @@ internal class SFSessionHttpClientProperties private TimeSpan _expirationTimeout; private bool _poolingEnabled; internal bool _clientStoreTemporaryCredential; - internal int _connectionLimit; + internal int _servicePointConnectionLimit; public static SFSessionHttpClientProperties ExtractAndValidate(SFSessionProperties properties) { @@ -186,10 +186,10 @@ private void ValidateWaitingForSessionIdleTimeout() private void ValidateConnectionLimit() { - if (_connectionLimit < 1) + if (_servicePointConnectionLimit < 1) { s_logger.Warn($"Connection limit cannot be less than 1. Using the default value of {DefaultConnectionLimit}"); - _connectionLimit = DefaultConnectionLimit; + _servicePointConnectionLimit = DefaultConnectionLimit; } } @@ -205,7 +205,7 @@ public HttpClientConfig BuildHttpClientConfig() disableRetry, forceRetryOn404, maxHttpRetries, - _connectionLimit, + _servicePointConnectionLimit, includeRetryReason); } @@ -267,7 +267,7 @@ public SFSessionHttpClientProperties ExtractProperties(SFSessionProperties prope _poolingEnabled = extractor.ExtractBooleanWithDefaultValue(SFSessionProperty.POOLINGENABLED), _disableSamlUrlCheck = extractor.ExtractBooleanWithDefaultValue(SFSessionProperty.DISABLE_SAML_URL_CHECK), _clientStoreTemporaryCredential = Boolean.Parse(propertiesDictionary[SFSessionProperty.CLIENT_STORE_TEMPORARY_CREDENTIAL]), - _connectionLimit = int.Parse(propertiesDictionary[SFSessionProperty.CONNECTION_LIMIT]), + _servicePointConnectionLimit = int.Parse(propertiesDictionary[SFSessionProperty.SERVICE_POINT_CONNECTION_LIMIT]), }; } diff --git a/Snowflake.Data/Core/Session/SFSessionProperty.cs b/Snowflake.Data/Core/Session/SFSessionProperty.cs index 25f7eb067..b7c2a3010 100644 --- a/Snowflake.Data/Core/Session/SFSessionProperty.cs +++ b/Snowflake.Data/Core/Session/SFSessionProperty.cs @@ -134,7 +134,7 @@ internal enum SFSessionProperty [SFSessionPropertyAttr(required = false, defaultValue = "false")] OAUTHENABLESINGLEUSEREFRESHTOKENS, [SFSessionPropertyAttr(required = false, defaultValue = "20")] - CONNECTION_LIMIT, + SERVICE_POINT_CONNECTION_LIMIT, } class SFSessionPropertyAttr : Attribute diff --git a/doc/Connecting.md b/doc/Connecting.md index f273ce573..48b7041d0 100644 --- a/doc/Connecting.md +++ b/doc/Connecting.md @@ -62,7 +62,7 @@ The following table lists all valid connection properties: | WORKLOAD_IDENTITY_PROVIDER | Depends | The type of attestation provider for Workload Identity Federation authentication. You can specify one of following values: `OIDC`, `AZURE`, `AWS`, `GCP`. It is mandatory when Authenticator is `workload_identity`. | | WORKLOAD_IDENTITY_ENTRA_RESOURCE | No | The entra resource used for Azure provider in Workload Identity Federation authentication. The default value for it is `api://fd3f753b-eed3-462c-b6a7-a4b5bb650aad`. | | OAUTHENABLESINGLEUSEREFRESHTOKENS | No | Used in OAuth Authorization Code Flow authentication. The default value is `false`. When set to `true` the driver requests the Identity Provider for single use refresh tokens. | -| CONNECTION_LIMIT | No | The maximum amount of connections allowed for the ServicePoint object. The default value is 20. | +| SERVICE_POINT_CONNECTION_LIMIT | No | The maximum amount of connections allowed for the ServicePoint object. The default value is 20. |
**Note**: Connections should not be shared across multiple threads. From 63cfb78c444a28684ef266338485791bd717ce9c Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 12 Sep 2025 11:05:44 -0700 Subject: [PATCH 09/20] Add more detail to docs about the connection limit --- doc/Connecting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Connecting.md b/doc/Connecting.md index 48b7041d0..4d97cb40a 100644 --- a/doc/Connecting.md +++ b/doc/Connecting.md @@ -62,7 +62,7 @@ The following table lists all valid connection properties: | WORKLOAD_IDENTITY_PROVIDER | Depends | The type of attestation provider for Workload Identity Federation authentication. You can specify one of following values: `OIDC`, `AZURE`, `AWS`, `GCP`. It is mandatory when Authenticator is `workload_identity`. | | WORKLOAD_IDENTITY_ENTRA_RESOURCE | No | The entra resource used for Azure provider in Workload Identity Federation authentication. The default value for it is `api://fd3f753b-eed3-462c-b6a7-a4b5bb650aad`. | | OAUTHENABLESINGLEUSEREFRESHTOKENS | No | Used in OAuth Authorization Code Flow authentication. The default value is `false`. When set to `true` the driver requests the Identity Provider for single use refresh tokens. | -| SERVICE_POINT_CONNECTION_LIMIT | No | The maximum amount of connections allowed for the ServicePoint object. The default value is 20. | +| SERVICE_POINT_CONNECTION_LIMIT | No | The maximum amount of connections allowed for the ServicePoint object. The default value is 20. Note: Only the connection limit from the first connection string is used. If other connection strings have different limits, they will be ignored because the HTTP client from the first connection is reused. |
**Note**: Connections should not be shared across multiple threads. From 3870ebc5df2e8d7c1f7d0d56586e4cfc85d4c509 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Tue, 16 Sep 2025 10:55:51 -0700 Subject: [PATCH 10/20] Fix connection string --- .../UnitTests/Session/SFHttpClientPropertiesTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs index 79237ced4..a261abb57 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs @@ -57,7 +57,7 @@ public void TestConvertToMapOnly2Properties( public void TestSettingConnectionLimitProperty(int expectedConnectionLimit) { // arrange - var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;CONNECTION_LIMIT={expectedConnectionLimit}"; + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;SERVICE_POINT_CONNECTION_LIMIT={expectedConnectionLimit}"; var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); // act From 25ba9d01307e799ea0952c8d976cf19322ef3c47 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Tue, 16 Sep 2025 11:07:02 -0700 Subject: [PATCH 11/20] Change error thrown to SnowflakeDbException --- .../UnitTests/Session/SFHttpClientPropertiesTest.cs | 9 +++++++-- .../Core/Session/SFSessionHttpClientProperties.cs | 2 +- .../SessionPropertiesWithDefaultValuesExtractor.cs | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs index a261abb57..f7c8123aa 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; +using Snowflake.Data.Client; using Snowflake.Data.Core; using Snowflake.Data.Core.Session; using Snowflake.Data.Core.Tools; @@ -99,13 +100,17 @@ public void TestSettingConnectionLimitPropertyToNoValue() public void TestThrowsExceptionWhenSettingConnectionLimitPropertyToNonStringValue() { // arrange - var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;SERVICE_POINT_CONNECTION_LIMIT=abc"; + var parameterName = "SERVICE_POINT_CONNECTION_LIMIT"; + var errorMessage = $"Error: Invalid parameter value for {parameterName}"; + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;{parameterName}=abc"; // act var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); // assert - var thrown = Assert.Throws(() => SFSessionHttpClientProperties.ExtractAndValidate(properties)); + var thrown = Assert.Throws(() => SFSessionHttpClientProperties.ExtractAndValidate(properties)); + Assert.AreEqual(SFError.INVALID_CONNECTION_PARAMETER_VALUE.GetAttribute().errorCode, thrown.ErrorCode); + Assert.IsTrue(thrown.Message.Contains(errorMessage)); } [Test] diff --git a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs index 70a1f59a6..5a328d186 100644 --- a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs +++ b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs @@ -267,7 +267,7 @@ public SFSessionHttpClientProperties ExtractProperties(SFSessionProperties prope _poolingEnabled = extractor.ExtractBooleanWithDefaultValue(SFSessionProperty.POOLINGENABLED), _disableSamlUrlCheck = extractor.ExtractBooleanWithDefaultValue(SFSessionProperty.DISABLE_SAML_URL_CHECK), _clientStoreTemporaryCredential = Boolean.Parse(propertiesDictionary[SFSessionProperty.CLIENT_STORE_TEMPORARY_CREDENTIAL]), - _servicePointConnectionLimit = int.Parse(propertiesDictionary[SFSessionProperty.SERVICE_POINT_CONNECTION_LIMIT]), + _servicePointConnectionLimit = extractor.ExtractNonNegativeIntegerWithDefaultValue(SFSessionProperty.SERVICE_POINT_CONNECTION_LIMIT), }; } diff --git a/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs b/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs index a78e10105..01c56096e 100644 --- a/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs +++ b/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Text.RegularExpressions; +using Snowflake.Data.Client; using Snowflake.Data.Core.Tools; using Snowflake.Data.Log; @@ -88,7 +89,7 @@ public T ExtractPropertyWithDefaultValue( if (_failOnWrongValue) { s_logger.Error($"Invalid value of parameter {property}. Error: {e}"); - throw new Exception($"Invalid value of parameter {property}", e); + throw new SnowflakeDbException(SFError.INVALID_CONNECTION_PARAMETER_VALUE, "", property); } s_logger.Warn($"Invalid value of parameter {property}. Using a default a default value: {defaultValue}"); return defaultValue; From bf78e92cc460559be6218c59c377c9e08cf8bcb0 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Tue, 16 Sep 2025 11:50:14 -0700 Subject: [PATCH 12/20] Modify tests for SnowflakeDbException --- .../ConnectionPoolConfigExtractorTest.cs | 24 +++++++++---------- ...ionPropertiesWithDefaultValuesExtractor.cs | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/ConnectionPoolConfigExtractorTest.cs b/Snowflake.Data.Tests/UnitTests/Session/ConnectionPoolConfigExtractorTest.cs index 32a475bb4..1ddcfb999 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/ConnectionPoolConfigExtractorTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/ConnectionPoolConfigExtractorTest.cs @@ -55,10 +55,10 @@ public void TestExtractFailsForWrongValueOfMaxPoolSize(string maxPoolSize) var connectionString = $"account=test;user=test;password=test;maxPoolSize={maxPoolSize}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.MAXPOOLSIZE.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.MAXPOOLSIZE.ToString()}")); } [Test] @@ -86,10 +86,10 @@ public void TestExtractFailsForWrongValueOfMinPoolSize(string minPoolSize) var connectionString = $"account=test;user=test;password=test;minPoolSize={minPoolSize}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.MINPOOLSIZE.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.MINPOOLSIZE.ToString()}")); } [Test] @@ -127,10 +127,10 @@ public void TestExtractExpirationTimeoutFailsWhenWrongValue(string propertyValue var connectionString = $"account=test;user=test;password=test;expirationTimeout={propertyValue}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.EXPIRATIONTIMEOUT.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.EXPIRATIONTIMEOUT.ToString()}")); } [Test] @@ -168,10 +168,10 @@ public void TestExtractWaitingForIdleSessionTimeoutFailsWhenWrongValue(string pr var connectionString = $"account=test;user=test;password=test;waitingForIdleSessionTimeout={propertyValue}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.WAITINGFORIDLESESSIONTIMEOUT.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.WAITINGFORIDLESESSIONTIMEOUT.ToString()}")); } [Test] @@ -196,10 +196,10 @@ public void TestExtractConnectionTimeoutFailsForWrongValue(string propertyValue) var connectionString = $"account=test;user=test;password=test;CONNECTION_TIMEOUT={propertyValue}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.CONNECTION_TIMEOUT.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.CONNECTION_TIMEOUT.ToString()}")); } [Test] @@ -250,10 +250,10 @@ public void TestExtractFailsForWrongValueOfPoolingEnabled(string propertyValue) var connectionString = $"account=test;user=test;password=test;poolingEnabled={propertyValue}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.POOLINGENABLED.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.POOLINGENABLED.ToString()}")); } [Test] diff --git a/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs b/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs index 01c56096e..742f5148e 100644 --- a/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs +++ b/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs @@ -109,7 +109,7 @@ private TResult handleFailedValidation( if (_failOnWrongValue) { s_logger.Error($"Invalid value of parameter {property}: {value}"); - throw new Exception($"Invalid value of parameter {property}"); + throw new SnowflakeDbException(SFError.INVALID_CONNECTION_PARAMETER_VALUE, "", property); } s_logger.Warn($"Invalid value of parameter {property}. Using a default value: {defaultValue}"); return defaultValue; From 53ad053897381efce0ba2f35fd0b46105606730a Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Tue, 16 Sep 2025 12:30:09 -0700 Subject: [PATCH 13/20] Modify tests for SnowflakeDbException --- ...ssionPropertiesWithDefaultValuesExtractorTest.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SessionPropertiesWithDefaultValuesExtractorTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SessionPropertiesWithDefaultValuesExtractorTest.cs index 9475c5a24..40e07fd62 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SessionPropertiesWithDefaultValuesExtractorTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SessionPropertiesWithDefaultValuesExtractorTest.cs @@ -1,5 +1,6 @@ using System; using NUnit.Framework; +using Snowflake.Data.Client; using Snowflake.Data.Core; using Snowflake.Data.Core.Session; @@ -154,7 +155,7 @@ public void TestFailWhenPreValidationFails() var extractor = new SessionPropertiesWithDefaultValuesExtractor(properties, true); // act - var thrown = Assert.Throws(() => + var thrown = Assert.Throws(() => extractor.ExtractPropertyWithDefaultValue( SFSessionProperty.CONNECTION_TIMEOUT, int.Parse, @@ -163,7 +164,7 @@ public void TestFailWhenPreValidationFails() )); // assert - Assert.That(thrown.Message, Does.Contain("Invalid value of parameter CONNECTION_TIMEOUT")); + Assert.That(thrown.Message, Does.Contain("Invalid parameter value for CONNECTION_TIMEOUT")); } [Test] @@ -175,7 +176,7 @@ public void TestFailWhenPostValidationFails() var defaultValue = GetDefaultIntSessionProperty(SFSessionProperty.CONNECTION_TIMEOUT); // act - var thrown = Assert.Throws(() => + var thrown = Assert.Throws(() => extractor.ExtractPropertyWithDefaultValue( SFSessionProperty.CONNECTION_TIMEOUT, int.Parse, @@ -184,7 +185,7 @@ public void TestFailWhenPostValidationFails() )); // assert - Assert.That(thrown.Message, Does.Contain("Invalid value of parameter CONNECTION_TIMEOUT")); + Assert.That(thrown.Message, Does.Contain("Invalid parameter value for CONNECTION_TIMEOUT")); } [Test] @@ -195,7 +196,7 @@ public void TestFailWhenExtractFails() var extractor = new SessionPropertiesWithDefaultValuesExtractor(properties, true); // act - var thrown = Assert.Throws(() => + var thrown = Assert.Throws(() => extractor.ExtractPropertyWithDefaultValue( SFSessionProperty.CONNECTION_TIMEOUT, int.Parse, @@ -204,7 +205,7 @@ public void TestFailWhenExtractFails() )); // assert - Assert.That(thrown.Message, Does.Contain("Invalid value of parameter CONNECTION_TIMEOUT")); + Assert.That(thrown.Message, Does.Contain("Invalid parameter value for CONNECTION_TIMEOUT")); } private int GetDefaultIntSessionProperty(SFSessionProperty property) => From 22402afdbf891eb68c0a32b0cbf0d4bd50266a5c Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Wed, 17 Sep 2025 10:42:01 -0700 Subject: [PATCH 14/20] Revert "Change error thrown to SnowflakeDbException" This reverts commit 25ba9d01307e799ea0952c8d976cf19322ef3c47. --- .../UnitTests/Session/SFHttpClientPropertiesTest.cs | 9 ++------- .../Core/Session/SFSessionHttpClientProperties.cs | 2 +- .../SessionPropertiesWithDefaultValuesExtractor.cs | 3 +-- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs index f7c8123aa..a261abb57 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using Snowflake.Data.Client; using Snowflake.Data.Core; using Snowflake.Data.Core.Session; using Snowflake.Data.Core.Tools; @@ -100,17 +99,13 @@ public void TestSettingConnectionLimitPropertyToNoValue() public void TestThrowsExceptionWhenSettingConnectionLimitPropertyToNonStringValue() { // arrange - var parameterName = "SERVICE_POINT_CONNECTION_LIMIT"; - var errorMessage = $"Error: Invalid parameter value for {parameterName}"; - var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;{parameterName}=abc"; + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;SERVICE_POINT_CONNECTION_LIMIT=abc"; // act var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); // assert - var thrown = Assert.Throws(() => SFSessionHttpClientProperties.ExtractAndValidate(properties)); - Assert.AreEqual(SFError.INVALID_CONNECTION_PARAMETER_VALUE.GetAttribute().errorCode, thrown.ErrorCode); - Assert.IsTrue(thrown.Message.Contains(errorMessage)); + var thrown = Assert.Throws(() => SFSessionHttpClientProperties.ExtractAndValidate(properties)); } [Test] diff --git a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs index 5a328d186..70a1f59a6 100644 --- a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs +++ b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs @@ -267,7 +267,7 @@ public SFSessionHttpClientProperties ExtractProperties(SFSessionProperties prope _poolingEnabled = extractor.ExtractBooleanWithDefaultValue(SFSessionProperty.POOLINGENABLED), _disableSamlUrlCheck = extractor.ExtractBooleanWithDefaultValue(SFSessionProperty.DISABLE_SAML_URL_CHECK), _clientStoreTemporaryCredential = Boolean.Parse(propertiesDictionary[SFSessionProperty.CLIENT_STORE_TEMPORARY_CREDENTIAL]), - _servicePointConnectionLimit = extractor.ExtractNonNegativeIntegerWithDefaultValue(SFSessionProperty.SERVICE_POINT_CONNECTION_LIMIT), + _servicePointConnectionLimit = int.Parse(propertiesDictionary[SFSessionProperty.SERVICE_POINT_CONNECTION_LIMIT]), }; } diff --git a/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs b/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs index 742f5148e..65bf5878e 100644 --- a/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs +++ b/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Text.RegularExpressions; -using Snowflake.Data.Client; using Snowflake.Data.Core.Tools; using Snowflake.Data.Log; @@ -89,7 +88,7 @@ public T ExtractPropertyWithDefaultValue( if (_failOnWrongValue) { s_logger.Error($"Invalid value of parameter {property}. Error: {e}"); - throw new SnowflakeDbException(SFError.INVALID_CONNECTION_PARAMETER_VALUE, "", property); + throw new Exception($"Invalid value of parameter {property}", e); } s_logger.Warn($"Invalid value of parameter {property}. Using a default a default value: {defaultValue}"); return defaultValue; From 9bbaa193784ae4b04e813074071eadc4225c47c3 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Wed, 17 Sep 2025 10:43:47 -0700 Subject: [PATCH 15/20] Revert "Modify tests for SnowflakeDbException" This reverts commit 53ad053897381efce0ba2f35fd0b46105606730a. --- ...ssionPropertiesWithDefaultValuesExtractorTest.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SessionPropertiesWithDefaultValuesExtractorTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SessionPropertiesWithDefaultValuesExtractorTest.cs index 40e07fd62..9475c5a24 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SessionPropertiesWithDefaultValuesExtractorTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SessionPropertiesWithDefaultValuesExtractorTest.cs @@ -1,6 +1,5 @@ using System; using NUnit.Framework; -using Snowflake.Data.Client; using Snowflake.Data.Core; using Snowflake.Data.Core.Session; @@ -155,7 +154,7 @@ public void TestFailWhenPreValidationFails() var extractor = new SessionPropertiesWithDefaultValuesExtractor(properties, true); // act - var thrown = Assert.Throws(() => + var thrown = Assert.Throws(() => extractor.ExtractPropertyWithDefaultValue( SFSessionProperty.CONNECTION_TIMEOUT, int.Parse, @@ -164,7 +163,7 @@ public void TestFailWhenPreValidationFails() )); // assert - Assert.That(thrown.Message, Does.Contain("Invalid parameter value for CONNECTION_TIMEOUT")); + Assert.That(thrown.Message, Does.Contain("Invalid value of parameter CONNECTION_TIMEOUT")); } [Test] @@ -176,7 +175,7 @@ public void TestFailWhenPostValidationFails() var defaultValue = GetDefaultIntSessionProperty(SFSessionProperty.CONNECTION_TIMEOUT); // act - var thrown = Assert.Throws(() => + var thrown = Assert.Throws(() => extractor.ExtractPropertyWithDefaultValue( SFSessionProperty.CONNECTION_TIMEOUT, int.Parse, @@ -185,7 +184,7 @@ public void TestFailWhenPostValidationFails() )); // assert - Assert.That(thrown.Message, Does.Contain("Invalid parameter value for CONNECTION_TIMEOUT")); + Assert.That(thrown.Message, Does.Contain("Invalid value of parameter CONNECTION_TIMEOUT")); } [Test] @@ -196,7 +195,7 @@ public void TestFailWhenExtractFails() var extractor = new SessionPropertiesWithDefaultValuesExtractor(properties, true); // act - var thrown = Assert.Throws(() => + var thrown = Assert.Throws(() => extractor.ExtractPropertyWithDefaultValue( SFSessionProperty.CONNECTION_TIMEOUT, int.Parse, @@ -205,7 +204,7 @@ public void TestFailWhenExtractFails() )); // assert - Assert.That(thrown.Message, Does.Contain("Invalid parameter value for CONNECTION_TIMEOUT")); + Assert.That(thrown.Message, Does.Contain("Invalid value of parameter CONNECTION_TIMEOUT")); } private int GetDefaultIntSessionProperty(SFSessionProperty property) => From d5009808f550e47beb1dc92bd9b742cc908eea16 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Wed, 17 Sep 2025 10:44:03 -0700 Subject: [PATCH 16/20] Revert "Modify tests for SnowflakeDbException" This reverts commit bf78e92cc460559be6218c59c377c9e08cf8bcb0. --- .../ConnectionPoolConfigExtractorTest.cs | 24 +++++++++---------- ...ionPropertiesWithDefaultValuesExtractor.cs | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/ConnectionPoolConfigExtractorTest.cs b/Snowflake.Data.Tests/UnitTests/Session/ConnectionPoolConfigExtractorTest.cs index 1ddcfb999..32a475bb4 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/ConnectionPoolConfigExtractorTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/ConnectionPoolConfigExtractorTest.cs @@ -55,10 +55,10 @@ public void TestExtractFailsForWrongValueOfMaxPoolSize(string maxPoolSize) var connectionString = $"account=test;user=test;password=test;maxPoolSize={maxPoolSize}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.MAXPOOLSIZE.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.MAXPOOLSIZE.ToString()}")); } [Test] @@ -86,10 +86,10 @@ public void TestExtractFailsForWrongValueOfMinPoolSize(string minPoolSize) var connectionString = $"account=test;user=test;password=test;minPoolSize={minPoolSize}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.MINPOOLSIZE.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.MINPOOLSIZE.ToString()}")); } [Test] @@ -127,10 +127,10 @@ public void TestExtractExpirationTimeoutFailsWhenWrongValue(string propertyValue var connectionString = $"account=test;user=test;password=test;expirationTimeout={propertyValue}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.EXPIRATIONTIMEOUT.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.EXPIRATIONTIMEOUT.ToString()}")); } [Test] @@ -168,10 +168,10 @@ public void TestExtractWaitingForIdleSessionTimeoutFailsWhenWrongValue(string pr var connectionString = $"account=test;user=test;password=test;waitingForIdleSessionTimeout={propertyValue}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.WAITINGFORIDLESESSIONTIMEOUT.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.WAITINGFORIDLESESSIONTIMEOUT.ToString()}")); } [Test] @@ -196,10 +196,10 @@ public void TestExtractConnectionTimeoutFailsForWrongValue(string propertyValue) var connectionString = $"account=test;user=test;password=test;CONNECTION_TIMEOUT={propertyValue}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.CONNECTION_TIMEOUT.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.CONNECTION_TIMEOUT.ToString()}")); } [Test] @@ -250,10 +250,10 @@ public void TestExtractFailsForWrongValueOfPoolingEnabled(string propertyValue) var connectionString = $"account=test;user=test;password=test;poolingEnabled={propertyValue}"; // act - var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); + var thrown = Assert.Throws(() => ExtractConnectionPoolConfig(connectionString)); // assert - Assert.That(thrown.Message, Does.Contain($"Invalid parameter value for {SFSessionProperty.POOLINGENABLED.ToString()}")); + Assert.That(thrown.Message, Does.Contain($"Invalid value of parameter {SFSessionProperty.POOLINGENABLED.ToString()}")); } [Test] diff --git a/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs b/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs index 65bf5878e..a78e10105 100644 --- a/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs +++ b/Snowflake.Data/Core/Session/SessionPropertiesWithDefaultValuesExtractor.cs @@ -108,7 +108,7 @@ private TResult handleFailedValidation( if (_failOnWrongValue) { s_logger.Error($"Invalid value of parameter {property}: {value}"); - throw new SnowflakeDbException(SFError.INVALID_CONNECTION_PARAMETER_VALUE, "", property); + throw new Exception($"Invalid value of parameter {property}"); } s_logger.Warn($"Invalid value of parameter {property}. Using a default value: {defaultValue}"); return defaultValue; From 52b2004b99673114b2be7e2b53905ffb2985cabd Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Wed, 17 Sep 2025 11:04:25 -0700 Subject: [PATCH 17/20] Validate property when parsing connection string --- .../Session/SFHttpClientPropertiesTest.cs | 10 +++++++--- .../Core/Session/SFSessionProperty.cs | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs index a261abb57..948be9fe5 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; +using Snowflake.Data.Client; using Snowflake.Data.Core; using Snowflake.Data.Core.Session; using Snowflake.Data.Core.Tools; @@ -99,13 +100,16 @@ public void TestSettingConnectionLimitPropertyToNoValue() public void TestThrowsExceptionWhenSettingConnectionLimitPropertyToNonStringValue() { // arrange - var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;SERVICE_POINT_CONNECTION_LIMIT=abc"; + var parameterName = "SERVICE_POINT_CONNECTION_LIMIT"; + var expectedErrorMessage = $"Error: Invalid parameter value for {parameterName}"; + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;{parameterName}=abc"; // act - var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); + var thrown = Assert.Throws(() => SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext())); // assert - var thrown = Assert.Throws(() => SFSessionHttpClientProperties.ExtractAndValidate(properties)); + Assert.AreEqual(SFError.INVALID_CONNECTION_PARAMETER_VALUE.GetAttribute().errorCode, thrown.ErrorCode); + Assert.IsTrue(thrown.Message.Contains(expectedErrorMessage)); } [Test] diff --git a/Snowflake.Data/Core/Session/SFSessionProperty.cs b/Snowflake.Data/Core/Session/SFSessionProperty.cs index b7c2a3010..a31d3d8eb 100644 --- a/Snowflake.Data/Core/Session/SFSessionProperty.cs +++ b/Snowflake.Data/Core/Session/SFSessionProperty.cs @@ -286,6 +286,7 @@ internal static SFSessionProperties ParseConnectionString(string connectionStrin ValidateAccountDomain(properties); WarnIfHttpUsed(properties); ValidateAuthenticatorFlowsProperties(properties); + ValidateServicePointConnectionLimit(properties); var allowUnderscoresInHost = ParseAllowUnderscoresInHost(properties); @@ -766,6 +767,23 @@ private static void ValidateFileTransferMaxBytesInMemoryProperty(SFSessionProper } } + private static void ValidateServicePointConnectionLimit(SFSessionProperties properties) + { + if (properties.TryGetValue(SFSessionProperty.SERVICE_POINT_CONNECTION_LIMIT, out var servicePointConnectionLimit)) + { + if (!int.TryParse(servicePointConnectionLimit, out _)) + { + var errorMessage = $"Invalid value of {SFSessionProperty.SERVICE_POINT_CONNECTION_LIMIT.ToString()} parameter"; + logger.Error(errorMessage); + throw new SnowflakeDbException( + new Exception(errorMessage), + SFError.INVALID_CONNECTION_PARAMETER_VALUE, + "", + SFSessionProperty.SERVICE_POINT_CONNECTION_LIMIT.ToString()); + } + } + } + private static bool IsRequired(SFSessionProperty sessionProperty, SFSessionProperties properties) { if (sessionProperty.Equals(SFSessionProperty.PASSWORD)) From 81b029a2cddc5e24f117ce6012bd4fb301d5c4d5 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Thu, 16 Oct 2025 10:51:30 -0700 Subject: [PATCH 18/20] Rename test and add more cases --- .../UnitTests/Session/SFHttpClientPropertiesTest.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs index 31c0ddc3f..137acf220 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs @@ -99,12 +99,17 @@ public void TestSettingConnectionLimitPropertyToNoValue() } [Test] - public void TestThrowsExceptionWhenSettingConnectionLimitPropertyToNonStringValue() + [TestCase("abc")] + [TestCase("1.5")] + [TestCase("true")] + [TestCase("-2.3")] + [TestCase("null")] + public void TestThrowsExceptionWhenSettingConnectionLimitPropertyToNonIntegerValue(string nonIntegerValue) { // arrange var parameterName = "SERVICE_POINT_CONNECTION_LIMIT"; var expectedErrorMessage = $"Error: Invalid parameter value for {parameterName}"; - var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;{parameterName}=abc"; + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;{parameterName}={nonIntegerValue}"; // act var thrown = Assert.Throws(() => SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext())); From 62b0922028427a9058725931380934e3ee5d1087 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Thu, 16 Oct 2025 10:51:56 -0700 Subject: [PATCH 19/20] Modified warning log --- Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs index fec3423fb..0ba09560a 100644 --- a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs +++ b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs @@ -195,7 +195,7 @@ private void ValidateConnectionLimit() { if (_servicePointConnectionLimit < 1) { - s_logger.Warn($"Connection limit cannot be less than 1. Using the default value of {DefaultConnectionLimit}"); + s_logger.Warn($"Connection limit must be a positive integer. Using the default value of {DefaultConnectionLimit}"); _servicePointConnectionLimit = DefaultConnectionLimit; } } From 8ed4cca2fcc06426f9878a78b8a10d8c24507275 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Thu, 16 Oct 2025 11:02:53 -0700 Subject: [PATCH 20/20] Add upper limit for connection --- .../Session/SFHttpClientPropertiesTest.cs | 14 ++++++++++++++ .../Core/Session/SFSessionHttpClientProperties.cs | 5 +++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs index 137acf220..eed04e4bf 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs @@ -84,6 +84,20 @@ public void TestSettingConnectionLimitPropertyToLessThan1() Assert.AreEqual(SFSessionHttpClientProperties.DefaultConnectionLimit, extractedProperties._servicePointConnectionLimit); } + [Test] + public void TestSettingConnectionLimitPropertyToGreaterThanMaxConnectionLimit() + { + // arrange + var connectionString = $"ACCOUNT=account;USER=test;PASSWORD=test;SERVICE_POINT_CONNECTION_LIMIT={SFSessionHttpClientProperties.MaxConnectionLimit + 1}"; + var properties = SFSessionProperties.ParseConnectionString(connectionString, new SessionPropertiesContext()); + + // act + var extractedProperties = SFSessionHttpClientProperties.ExtractAndValidate(properties); + + // assert + Assert.AreEqual(SFSessionHttpClientProperties.DefaultConnectionLimit, extractedProperties._servicePointConnectionLimit); + } + [Test] public void TestSettingConnectionLimitPropertyToNoValue() { diff --git a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs index 0ba09560a..e2fcf3b45 100644 --- a/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs +++ b/Snowflake.Data/Core/Session/SFSessionHttpClientProperties.cs @@ -24,6 +24,7 @@ internal class SFSessionHttpClientProperties public const int DefaultConnectionLimit = 20; public static readonly TimeSpan DefaultRetryTimeout = TimeSpan.FromSeconds(300); private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger(); + internal static readonly int MaxConnectionLimit = 1000; internal bool validateDefaultParameters; internal bool clientSessionKeepAlive; @@ -193,9 +194,9 @@ private void ValidateWaitingForSessionIdleTimeout() private void ValidateConnectionLimit() { - if (_servicePointConnectionLimit < 1) + if (_servicePointConnectionLimit < 1 || _servicePointConnectionLimit > MaxConnectionLimit) { - s_logger.Warn($"Connection limit must be a positive integer. Using the default value of {DefaultConnectionLimit}"); + s_logger.Warn($"Connection limit must be between 1 and {MaxConnectionLimit}. Using the default value of {DefaultConnectionLimit}"); _servicePointConnectionLimit = DefaultConnectionLimit; } }