From 4629f011b501e629683d04b6fb11eff2682ef2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Hofman?= Date: Mon, 16 Oct 2023 23:32:46 +0200 Subject: [PATCH] SNOW-902608 integration tests for two versions of Connection Pool --- .../SFConnectionPoolAsyncIT.cs | 21 +- .../IntegrationTests/SFConnectionPoolIT.cs | 222 +++++++++++++++--- Snowflake.Data.Tests/Util/PoolConfig.cs | 4 + .../Client/SnowflakeDbConnectionPool.cs | 46 +++- .../Core/Session/ConnectionManagerV1.cs | 1 + .../Core/Session/ConnectionPoolManager.cs | 75 ++++-- .../Core/Session/ConnectionPoolType.cs | 8 + .../Core/Session/IConnectionManager.cs | 1 + 8 files changed, 305 insertions(+), 73 deletions(-) create mode 100644 Snowflake.Data/Core/Session/ConnectionPoolType.cs diff --git a/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolAsyncIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolAsyncIT.cs index f36741858..a3a9d8416 100644 --- a/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolAsyncIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolAsyncIT.cs @@ -2,21 +2,20 @@ * Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved. */ +using System; +using System.Data; +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using Moq; +using Snowflake.Data.Client; +using Snowflake.Data.Core; +using Snowflake.Data.Tests.Mock; using Snowflake.Data.Tests.Util; namespace Snowflake.Data.Tests.IntegrationTests { - using System; - using System.Data; - using System.Data.Common; - using System.Threading; - using System.Threading.Tasks; - using NUnit.Framework; - using Moq; - using Snowflake.Data.Client; - using Snowflake.Data.Core; - using Snowflake.Data.Tests.Mock; - [TestFixture, NonParallelizable] class SFConnectionPoolITAsync : SFBaseTestAsync { diff --git a/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolIT.cs index 9c5428331..5d9001d32 100644 --- a/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolIT.cs @@ -2,30 +2,46 @@ * Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved. */ +using System; +using System.Data; +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using Snowflake.Data.Core; +using Snowflake.Data.Client; +using Snowflake.Data.Core.Session; +using Snowflake.Data.Log; using Snowflake.Data.Tests.Util; namespace Snowflake.Data.Tests.IntegrationTests { - using System; - using System.Data; - using System.Data.Common; - using System.Threading; - using System.Threading.Tasks; - using NUnit.Framework; - using Snowflake.Data.Core; - using Snowflake.Data.Client; - - [TestFixture, NonParallelizable] + [TestFixture(ConnectionPoolType.SingleConnectionCache)] + [TestFixture(ConnectionPoolType.MultipleConnectionPool)] + [NonParallelizable] class SFConnectionPoolIT : SFBaseTest { - private static readonly PoolConfig s_previousPoolConfig = new PoolConfig(); - + private readonly ConnectionPoolType _connectionPoolTypeUnderTest; + private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger(); + private static PoolConfig s_previousPoolConfig; + + public SFConnectionPoolIT(ConnectionPoolType connectionPoolTypeUnderTest) + { + _connectionPoolTypeUnderTest = connectionPoolTypeUnderTest; + s_previousPoolConfig = new PoolConfig(); + SnowflakeDbConnectionPool.SetConnectionPoolVersion(connectionPoolTypeUnderTest); + } + [SetUp] - public void BeforeTest() + public new void BeforeTest() { - s_previousPoolConfig.Reset(); - SnowflakeDbConnectionPool.SetPooling(true); + SnowflakeDbConnectionPool.GetPool(ConnectionString); // to instantiate the pool used in tests + SnowflakeDbConnectionPool.GetPool(ConnectionString + " retryCount=1"); + SnowflakeDbConnectionPool.GetPool(ConnectionString + " retryCount=2"); + SnowflakeDbConnectionPool.SetPooling(true); // TODO: when no session pool created it doesn't do anything!!!! maybe this state should be at pool management layer, not session pool layer SnowflakeDbConnectionPool.ClearAllPools(); + s_logger.Debug($"---------------- BeforeTest ---------------------"); + s_logger.Debug($"Testing Pool Type: {SnowflakeDbConnectionPool.GetConnectionPoolVersion()}"); } [TearDown] @@ -69,6 +85,7 @@ static void ConcurrentPoolingHelper(string connectionString, bool closeConnectio const int PoolTimeout = 3; // reset to default settings in case it changed by other test cases + SnowflakeDbConnectionPool.GetPool(connectionString); // to instantiate pool SnowflakeDbConnectionPool.SetPooling(true); SnowflakeDbConnectionPool.SetMaxPoolSize(10); SnowflakeDbConnectionPool.ClearAllPools(); @@ -129,9 +146,7 @@ static void QueryExecutionThread(string connectionString, bool closeConnection) [Test] public void TestBasicConnectionPool() { - SnowflakeDbConnectionPool.SetPooling(true); SnowflakeDbConnectionPool.SetMaxPoolSize(1); - SnowflakeDbConnectionPool.ClearAllPools(); var conn1 = new SnowflakeDbConnection(ConnectionString); conn1.Open(); @@ -139,7 +154,7 @@ public void TestBasicConnectionPool() conn1.Close(); Assert.AreEqual(ConnectionState.Closed, conn1.State); - Assert.AreEqual(1, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); } [Test] @@ -150,24 +165,64 @@ public void TestConnectionPool() conn1.Open(); Assert.AreEqual(ConnectionState.Open, conn1.State); conn1.Close(); - Assert.AreEqual(1, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); var conn2 = new SnowflakeDbConnection(); conn2.ConnectionString = ConnectionString; conn2.Open(); Assert.AreEqual(ConnectionState.Open, conn2.State); - Assert.AreEqual(0, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(0, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); conn2.Close(); - Assert.AreEqual(1, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); Assert.AreEqual(ConnectionState.Closed, conn1.State); Assert.AreEqual(ConnectionState.Closed, conn2.State); SnowflakeDbConnectionPool.ClearAllPools(); } + // [Test] + // public void TestConnectionPoolIsFull() + // { + // TestOnlyForOldPool(); + // + // SnowflakeDbConnectionPool.SetPooling(true); + // SnowflakeDbConnectionPool.ClearAllPools(); + // SnowflakeDbConnectionPool.SetMaxPoolSize(2); + // var conn1 = new SnowflakeDbConnection(); + // conn1.ConnectionString = ConnectionString; + // conn1.Open(); + // Assert.AreEqual(ConnectionState.Open, conn1.State); + // + // var conn2 = new SnowflakeDbConnection(); + // conn2.ConnectionString = ConnectionString + " retryCount=1"; + // conn2.Open(); + // Assert.AreEqual(ConnectionState.Open, conn2.State); + // + // var conn3 = new SnowflakeDbConnection(); + // conn3.ConnectionString = ConnectionString + " retryCount=2"; + // conn3.Open(); + // Assert.AreEqual(ConnectionState.Open, conn3.State); + // SnowflakeDbConnectionPool.ClearAllPools(); + // + // conn1.Close(); + // Assert.AreEqual(1, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + // conn2.Close(); + // Assert.AreEqual(2, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + // conn3.Close(); + // Assert.AreEqual(2, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + // + // Assert.AreEqual(ConnectionState.Closed, conn1.State); + // Assert.AreEqual(ConnectionState.Closed, conn2.State); + // Assert.AreEqual(ConnectionState.Closed, conn3.State); + // SnowflakeDbConnectionPool.ClearAllPools(); + // } + [Test] - public void TestConnectionPoolIsFull() + public void TestNewConnectionPoolIsFull() { + // TestOnlyForNewPool(); + + var pool = SnowflakeDbConnectionPool.GetPool(ConnectionString); SnowflakeDbConnectionPool.SetPooling(true); SnowflakeDbConnectionPool.ClearAllPools(); SnowflakeDbConnectionPool.SetMaxPoolSize(2); @@ -177,22 +232,22 @@ public void TestConnectionPoolIsFull() Assert.AreEqual(ConnectionState.Open, conn1.State); var conn2 = new SnowflakeDbConnection(); - conn2.ConnectionString = ConnectionString + " retryCount=1"; + conn2.ConnectionString = ConnectionString; conn2.Open(); Assert.AreEqual(ConnectionState.Open, conn2.State); var conn3 = new SnowflakeDbConnection(); - conn3.ConnectionString = ConnectionString + " retryCount=2"; + conn3.ConnectionString = ConnectionString; conn3.Open(); Assert.AreEqual(ConnectionState.Open, conn3.State); SnowflakeDbConnectionPool.ClearAllPools(); conn1.Close(); - Assert.AreEqual(1, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(1, pool.GetCurrentPoolSize()); conn2.Close(); - Assert.AreEqual(2, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(2, pool.GetCurrentPoolSize()); conn3.Close(); - Assert.AreEqual(2, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(2, pool.GetCurrentPoolSize()); Assert.AreEqual(ConnectionState.Closed, conn1.State); Assert.AreEqual(ConnectionState.Closed, conn2.State); @@ -228,13 +283,15 @@ public void TestConnectionPoolExpirationWorks() // The pooling timeout should apply to all connections being pooled, // not just the connections created after the new setting, // so expected result should be 0 - Assert.AreEqual(0, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(0, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); SnowflakeDbConnectionPool.SetPooling(false); } [Test] public void TestConnectionPoolClean() { + TestOnlyForOldPool(); + SnowflakeDbConnectionPool.ClearAllPools(); SnowflakeDbConnectionPool.SetMaxPoolSize(2); var conn1 = new SnowflakeDbConnection(); @@ -266,9 +323,49 @@ public void TestConnectionPoolClean() SnowflakeDbConnectionPool.ClearAllPools(); } + [Test] + public void TestNewConnectionPoolClean() + { + TestOnlyForNewPool(); + + SnowflakeDbConnectionPool.ClearAllPools(); + SnowflakeDbConnectionPool.SetMaxPoolSize(2); + var conn1 = new SnowflakeDbConnection(); + conn1.ConnectionString = ConnectionString; + conn1.Open(); + Assert.AreEqual(ConnectionState.Open, conn1.State); + + var conn2 = new SnowflakeDbConnection(); + conn2.ConnectionString = ConnectionString + " retryCount=1"; + conn2.Open(); + Assert.AreEqual(ConnectionState.Open, conn2.State); + + var conn3 = new SnowflakeDbConnection(); + conn3.ConnectionString = ConnectionString + " retryCount=2"; + conn3.Open(); + Assert.AreEqual(ConnectionState.Open, conn3.State); + + conn1.Close(); + conn2.Close(); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(conn1.ConnectionString).GetCurrentPoolSize()); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(conn2.ConnectionString).GetCurrentPoolSize()); + SnowflakeDbConnectionPool.ClearAllPools(); + Assert.AreEqual(0, SnowflakeDbConnectionPool.GetPool(conn1.ConnectionString).GetCurrentPoolSize()); + Assert.AreEqual(0, SnowflakeDbConnectionPool.GetPool(conn2.ConnectionString).GetCurrentPoolSize()); + conn3.Close(); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(conn3.ConnectionString).GetCurrentPoolSize()); + + Assert.AreEqual(ConnectionState.Closed, conn1.State); + Assert.AreEqual(ConnectionState.Closed, conn2.State); + Assert.AreEqual(ConnectionState.Closed, conn3.State); + SnowflakeDbConnectionPool.ClearAllPools(); + } + [Test] public void TestConnectionPoolFull() { + TestOnlyForOldPool(); + SnowflakeDbConnectionPool.ClearAllPools(); SnowflakeDbConnectionPool.SetMaxPoolSize(2); SnowflakeDbConnectionPool.SetPooling(true); @@ -308,6 +405,53 @@ public void TestConnectionPoolFull() SnowflakeDbConnectionPool.ClearAllPools(); } + [Test] + public void TestNewConnectionPoolFull() + { + TestOnlyForNewPool(); + + var sessionPool = SnowflakeDbConnectionPool.GetPool(ConnectionString); + SnowflakeDbConnectionPool.ClearAllPools(); + SnowflakeDbConnectionPool.SetMaxPoolSize(2); + SnowflakeDbConnectionPool.SetPooling(true); + + var conn1 = new SnowflakeDbConnection(); + conn1.ConnectionString = ConnectionString; + conn1.Open(); + Assert.AreEqual(ConnectionState.Open, conn1.State); + + var conn2 = new SnowflakeDbConnection(); + conn2.ConnectionString = ConnectionString; + conn2.Open(); + Assert.AreEqual(ConnectionState.Open, conn2.State); + + Assert.AreEqual(0, sessionPool.GetCurrentPoolSize()); + conn1.Close(); + conn2.Close(); + Assert.AreEqual(2, sessionPool.GetCurrentPoolSize()); + + var conn3 = new SnowflakeDbConnection(); + conn3.ConnectionString = ConnectionString; + conn3.Open(); + Assert.AreEqual(ConnectionState.Open, conn3.State); + + var conn4 = new SnowflakeDbConnection(); + conn4.ConnectionString = ConnectionString; + conn4.Open(); + Assert.AreEqual(ConnectionState.Open, conn4.State); + + conn3.Close(); + Assert.AreEqual(1, sessionPool.GetCurrentPoolSize()); // TODO: when SNOW-937189 complete should be 2 + conn4.Close(); + Assert.AreEqual(2, sessionPool.GetCurrentPoolSize()); + + Assert.AreEqual(ConnectionState.Closed, conn1.State); + Assert.AreEqual(ConnectionState.Closed, conn2.State); + Assert.AreEqual(ConnectionState.Closed, conn3.State); + Assert.AreEqual(ConnectionState.Closed, conn4.State); + SnowflakeDbConnectionPool.ClearAllPools(); + } + [Test] public void TestConnectionPoolMultiThreading() { @@ -361,7 +505,7 @@ public void TestConnectionPoolDisable() conn1.Close(); Assert.AreEqual(ConnectionState.Closed, conn1.State); - Assert.AreEqual(0, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(0, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); } [Test] @@ -372,7 +516,7 @@ public void TestConnectionPoolWithDispose() SnowflakeDbConnectionPool.ClearAllPools(); var conn1 = new SnowflakeDbConnection(); - conn1.ConnectionString = ""; + conn1.ConnectionString = "bad connection string"; try { conn1.Open(); @@ -384,7 +528,7 @@ public void TestConnectionPoolWithDispose() } Assert.AreEqual(ConnectionState.Closed, conn1.State); - Assert.AreEqual(0, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(0, SnowflakeDbConnectionPool.GetPool(conn1.ConnectionString).GetCurrentPoolSize()); } [Test] @@ -402,10 +546,22 @@ public void TestConnectionPoolTurnOff() conn1.Close(); Assert.AreEqual(ConnectionState.Closed, conn1.State); - Assert.AreEqual(1, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); SnowflakeDbConnectionPool.SetPooling(false); //Put a breakpoint at SFSession close function, after connection pool is off, it will send close session request. } + + private void TestOnlyForOldPool() + { + if (_connectionPoolTypeUnderTest != ConnectionPoolType.SingleConnectionCache) + Assert.Ignore($"Test case relates only to {ConnectionPoolType.SingleConnectionCache} pool type"); + } + + private void TestOnlyForNewPool() + { + if (_connectionPoolTypeUnderTest != ConnectionPoolType.MultipleConnectionPool) + Assert.Ignore($"Test case relates only to {ConnectionPoolType.MultipleConnectionPool} pool type"); + } } -} +} \ No newline at end of file diff --git a/Snowflake.Data.Tests/Util/PoolConfig.cs b/Snowflake.Data.Tests/Util/PoolConfig.cs index 4856da243..b4fa6bc55 100644 --- a/Snowflake.Data.Tests/Util/PoolConfig.cs +++ b/Snowflake.Data.Tests/Util/PoolConfig.cs @@ -3,6 +3,7 @@ */ using Snowflake.Data.Client; +using Snowflake.Data.Core.Session; namespace Snowflake.Data.Tests.Util { @@ -11,12 +12,14 @@ class PoolConfig private readonly bool _pooling; private readonly long _timeout; private readonly int _maxPoolSize; + private readonly ConnectionPoolType _connectionPoolType; public PoolConfig() { _maxPoolSize = SnowflakeDbConnectionPool.GetMaxPoolSize(); _timeout = SnowflakeDbConnectionPool.GetTimeout(); _pooling = SnowflakeDbConnectionPool.GetPooling(); + _connectionPoolType = SnowflakeDbConnectionPool.GetConnectionPoolVersion(); } public void Reset() @@ -24,6 +27,7 @@ public void Reset() SnowflakeDbConnectionPool.SetMaxPoolSize(_maxPoolSize); SnowflakeDbConnectionPool.SetTimeout(_timeout); SnowflakeDbConnectionPool.SetPooling(_pooling); + SnowflakeDbConnectionPool.SetConnectionPoolVersion(_connectionPoolType); } } } diff --git a/Snowflake.Data/Client/SnowflakeDbConnectionPool.cs b/Snowflake.Data/Client/SnowflakeDbConnectionPool.cs index 54a3be0e7..5c5df3476 100644 --- a/Snowflake.Data/Client/SnowflakeDbConnectionPool.cs +++ b/Snowflake.Data/Client/SnowflakeDbConnectionPool.cs @@ -17,31 +17,36 @@ public class SnowflakeDbConnectionPool private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger(); private static readonly Object s_connectionManagerInstanceLock = new Object(); private static IConnectionManager s_connectionManager; + private static readonly ConnectionPoolType s_defaultConnectionPoolType = ConnectionPoolType.SingleConnectionCache; // TODO: set to public once development of entire ConnectionPoolManager epic is complete + private static IConnectionManager ConnectionManager { get { if (s_connectionManager != null) return s_connectionManager; - lock (s_connectionManagerInstanceLock) - { - s_connectionManager = new ConnectionManagerV1(); // TODO: change to ConnectionPoolManager once new pool implementation is complete - } + SetConnectionPoolVersion(s_defaultConnectionPoolType); return s_connectionManager; } } internal static SFSession GetSession(string connectionString, SecureString password) { - s_logger.Debug("SnowflakeDbConnectionPool::GetSession"); + s_logger.Debug($"SnowflakeDbConnectionPool::GetSession for {connectionString}"); return ConnectionManager.GetSession(connectionString, password); } internal static Task GetSessionAsync(string connectionString, SecureString password, CancellationToken cancellationToken) { - s_logger.Debug("SnowflakeDbConnectionPool::GetSessionAsync"); + s_logger.Debug($"SnowflakeDbConnectionPool::GetSessionAsync for {connectionString}"); return ConnectionManager.GetSessionAsync(connectionString, password, cancellationToken); } + + internal static SessionPool GetPool(string connectionString) + { + s_logger.Debug($"SnowflakeDbConnectionPool::GetPool"); + return ConnectionManager.GetPool(connectionString); + } internal static bool AddSession(SFSession session) { @@ -97,21 +102,40 @@ public static bool GetPooling() return ConnectionManager.GetPooling(); } - internal static void SwapVersion() // TODO: make public once development of entire ConnectionPoolManager is complete + internal static void SetOldConnectionPoolVersion() // TODO: set to public once development of entire ConnectionPoolManager epic is complete + { + SetConnectionPoolVersion(ConnectionPoolType.SingleConnectionCache); + } + + internal static void SetConnectionPoolVersion(ConnectionPoolType requestedPoolType) { lock (s_connectionManagerInstanceLock) { - if (ConnectionManager is ConnectionManagerV1) + s_connectionManager?.ClearAllPools(); + if (ConnectionPoolType.MultipleConnectionPool.Equals(requestedPoolType)) { - s_connectionManager.ClearAllPools(); s_connectionManager = new ConnectionPoolManager(); + s_logger.Info("SnowflakeDbConnectionPool - multiple connection pools enabled"); } - if (ConnectionManager is ConnectionPoolManager) + if (ConnectionPoolType.SingleConnectionCache.Equals(requestedPoolType)) { - s_connectionManager.ClearAllPools(); s_connectionManager = new ConnectionManagerV1(); + s_logger.Warn("SnowflakeDbConnectionPool - connection cache enabled"); + } + } + } + + internal static ConnectionPoolType GetConnectionPoolVersion() + { + if (ConnectionManager != null) + { + switch (ConnectionManager) + { + case ConnectionManagerV1 _: return ConnectionPoolType.SingleConnectionCache; + case ConnectionPoolManager _: return ConnectionPoolType.MultipleConnectionPool; } } + return s_defaultConnectionPoolType; } } } diff --git a/Snowflake.Data/Core/Session/ConnectionManagerV1.cs b/Snowflake.Data/Core/Session/ConnectionManagerV1.cs index 611ce449e..f06caea6f 100644 --- a/Snowflake.Data/Core/Session/ConnectionManagerV1.cs +++ b/Snowflake.Data/Core/Session/ConnectionManagerV1.cs @@ -23,5 +23,6 @@ public Task GetSessionAsync(string connectionString, SecureString pas public int GetCurrentPoolSize() => _sessionPool.GetCurrentPoolSize(); public bool SetPooling(bool poolingEnabled) => _sessionPool.SetPooling(poolingEnabled); public bool GetPooling() => _sessionPool.GetPooling(); + public SessionPool GetPool(string _) => _sessionPool; } } diff --git a/Snowflake.Data/Core/Session/ConnectionPoolManager.cs b/Snowflake.Data/Core/Session/ConnectionPoolManager.cs index 1de2d7f6e..6f4bd5063 100644 --- a/Snowflake.Data/Core/Session/ConnectionPoolManager.cs +++ b/Snowflake.Data/Core/Session/ConnectionPoolManager.cs @@ -4,9 +4,11 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Security; using System.Threading; using System.Threading.Tasks; +using Snowflake.Data.Client; using Snowflake.Data.Log; namespace Snowflake.Data.Core.Session @@ -25,17 +27,27 @@ internal ConnectionPoolManager() } } - public SFSession GetSession(string connectionString, SecureString password) - => GetPool(connectionString, password).GetSession(); - + public SFSession GetSession(string connectionString, SecureString password) + { + s_logger.Debug($"ConnectionPoolManager::GetSession for {connectionString}"); + return GetPool(connectionString, password).GetSession(); + } + public Task GetSessionAsync(string connectionString, SecureString password, CancellationToken cancellationToken) - => GetPool(connectionString, password).GetSessionAsync(cancellationToken); - - public bool AddSession(SFSession session) - => GetPool(session.ConnectionString, session.Password).AddSession(session); - + { + s_logger.Debug($"ConnectionPoolManager::GetSessionAsync for {connectionString}"); + return GetPool(connectionString, password).GetSessionAsync(cancellationToken); + } + + public bool AddSession(SFSession session) + { + s_logger.Debug($"ConnectionPoolManager::AddSession for {session.ConnectionString}"); + return GetPool(session.ConnectionString, session.Password).AddSession(session); + } + public void ClearAllPools() { + s_logger.Debug("ConnectionPoolManager::ClearAllPools"); foreach (var sessionPool in _pools.Values) { sessionPool.ClearAllPools(); @@ -44,28 +56,45 @@ public void ClearAllPools() public void SetMaxPoolSize(int maxPoolSize) { + s_logger.Debug("ConnectionPoolManager::SetMaxPoolSize for all pools"); foreach (var pool in _pools.Values) { pool.SetMaxPoolSize(maxPoolSize); } } - public int GetMaxPoolSize() => throw ApiNotSupportedException(); + public int GetMaxPoolSize() + { + s_logger.Debug("ConnectionPoolManager::GetMaxPoolSize"); + var values = _pools.Values.Select(it => it.GetMaxPoolSize()).Distinct().ToList(); + return values.Count == 1 + ? values.First() + : throw new SnowflakeDbException(SFError.INTERNAL_ERROR, "Multiple pools have different Max Pool Size values"); + } public void SetTimeout(long connectionTimeout) { + s_logger.Debug("ConnectionPoolManager::SetTimeout for all pools"); foreach (var pool in _pools.Values) { pool.SetTimeout(connectionTimeout); } } - public long GetTimeout() => throw ApiNotSupportedException(); + public long GetTimeout() + { + s_logger.Debug("ConnectionPoolManager::GetTimeout"); + var values = _pools.Values.Select(it => it.GetTimeout()).Distinct().ToList(); + return values.Count == 1 + ? values.First() + : throw new SnowflakeDbException(SFError.INTERNAL_ERROR, "Multiple pools have different Timeout values"); + } - public int GetCurrentPoolSize() => throw ApiNotSupportedException(); + public int GetCurrentPoolSize() => throw new SnowflakeDbException(SFError.INTERNAL_ERROR, "Multiple pools have different Current Pool Size values"); public bool SetPooling(bool poolingEnabled) { + s_logger.Debug("ConnectionPoolManager::SetPooling for all pools"); bool switched = true; foreach (var pool in _pools.Values) { @@ -75,29 +104,39 @@ public bool SetPooling(bool poolingEnabled) return switched; } - public bool GetPooling() => throw ApiNotSupportedException(); - - private NotSupportedException ApiNotSupportedException() + public bool GetPooling() { - var message = "Pool settings are controlled with connection string parameters or from a Session Pool object"; - s_logger.Error(message); - return new NotSupportedException(message); + s_logger.Debug("ConnectionPoolManager::GetPooling"); + var values = _pools.Values.Select(it => it.GetPooling()).Distinct().ToList(); + return values.Count == 1 + ? values.First() + : throw new SnowflakeDbException(SFError.INTERNAL_ERROR, "Multiple pools have different Pooling values"); } - + internal SessionPool GetPool(string connectionString, SecureString password) { + s_logger.Debug($"ConnectionPoolManager::GetPool for {connectionString}"); var poolKey = GetPoolKey(connectionString); if (_pools.TryGetValue(poolKey, out var item)) return item; lock (s_poolsLock) { + if (_pools.TryGetValue(poolKey, out var poolCreatedWhileWaitingOnLock)) + return poolCreatedWhileWaitingOnLock; + s_logger.Info($"Creating pool for connections to: {connectionString}"); var pool = SessionPool.CreateSessionPool(connectionString, password); _pools.Add(poolKey, pool); return pool; } } + public SessionPool GetPool(string connectionString) + { + s_logger.Debug($"ConnectionPoolManager::GetPool for {connectionString}"); + return GetPool(connectionString, null); + } + // TODO: SNOW-937188 private string GetPoolKey(string connectionString) { diff --git a/Snowflake.Data/Core/Session/ConnectionPoolType.cs b/Snowflake.Data/Core/Session/ConnectionPoolType.cs new file mode 100644 index 000000000..86a5aac36 --- /dev/null +++ b/Snowflake.Data/Core/Session/ConnectionPoolType.cs @@ -0,0 +1,8 @@ +namespace Snowflake.Data.Core.Session +{ + internal enum ConnectionPoolType + { + SingleConnectionCache, + MultipleConnectionPool + } +} \ No newline at end of file diff --git a/Snowflake.Data/Core/Session/IConnectionManager.cs b/Snowflake.Data/Core/Session/IConnectionManager.cs index e72ade2e7..c64699d54 100644 --- a/Snowflake.Data/Core/Session/IConnectionManager.cs +++ b/Snowflake.Data/Core/Session/IConnectionManager.cs @@ -21,5 +21,6 @@ internal interface IConnectionManager int GetCurrentPoolSize(); bool SetPooling(bool poolingEnabled); bool GetPooling(); + SessionPool GetPool(string connectionString); } }