Skip to content

Commit

Permalink
SNOW-902611
Browse files Browse the repository at this point in the history
- removed singleton pattern from SessionPool; introduced new interface for ConnectionManager; split tests of ConnectionPool and some cleanup in the classes
SNOW-902608
- new Connection Pool Manager version implementation
- unit tests for new pool; introduction of SessionFactory for unit testing of new pool manager and session pooling
- integration tests for two versions of Connection Pool
  • Loading branch information
sfc-gh-mhofman committed Oct 16, 2023
1 parent 7ff70e4 commit c4c4164
Show file tree
Hide file tree
Showing 12 changed files with 709 additions and 62 deletions.
212 changes: 190 additions & 22 deletions Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolIT.cs

Large diffs are not rendered by default.

215 changes: 215 additions & 0 deletions Snowflake.Data.Tests/UnitTests/ConnectionPoolManagerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* Copyright (c) 2023 Snowflake Computing Inc. All rights reserved.
*/

using System;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using Snowflake.Data.Core;
using Snowflake.Data.Core.Session;
using Moq;

namespace Snowflake.Data.Tests.UnitTests
{
[TestFixture, NonParallelizable]
class ConnectionPoolManagerTest
{
private readonly ConnectionPoolManager _connectionPoolManager = new ConnectionPoolManager();
private readonly string _connectionString1 = "database=D1;warehouse=W1;account=A1;user=U1;password=P1;role=R1;";
private readonly string _connectionString2 = "database=D2;warehouse=W2;account=A2;user=U2;password=P2;role=R2;";
private readonly SecureString _password = new SecureString();

[OneTimeSetUp]
public static void BeforeAllTests()
{
// SnowflakeDbConnectionPool.SwapVersion(); // TODO: swap when new version is the default
SessionPool.SessionFactory = new MockSessionFactory();
}

[OneTimeTearDown]
public void AfterAllTests()
{
SessionPool.SessionFactory = new SessionFactory();
}

[Test]
public void TestPoolManagerReturnsSessionPoolForGivenConnectionString()
{
// Act
var sessionPool = _connectionPoolManager.GetPool(_connectionString1, _password);

// Assert
Assert.AreEqual(_connectionString1, sessionPool.ConnectionString);
Assert.AreEqual(_password, sessionPool.Password);
}

[Test]
public void TestPoolManagerReturnsSamePoolForGivenConnectionString()
{
// Arrange
var anotherConnectionString = _connectionString1;

// Act
var sessionPool1 = _connectionPoolManager.GetPool(_connectionString1, _password);
var sessionPool2 = _connectionPoolManager.GetPool(anotherConnectionString, _password);

// Assert
Assert.AreEqual(sessionPool1, sessionPool2);
}

[Test]
public void TestDifferentPoolsAreReturnedForDifferentConnectionStrings()
{
// Arrange
Assert.AreNotSame(_connectionString1, _connectionString2);

// Act
var sessionPool1 = _connectionPoolManager.GetPool(_connectionString1, _password);
var sessionPool2 = _connectionPoolManager.GetPool(_connectionString2, _password);

// Assert
Assert.AreNotSame(sessionPool1, sessionPool2);
Assert.AreEqual(_connectionString1, sessionPool1.ConnectionString);
Assert.AreEqual(_connectionString2, sessionPool2.ConnectionString);
}


[Test]
public void TestGetSessionWorksForSpecifiedConnectionString()
{
// Act
var sfSession = _connectionPoolManager.GetSession(_connectionString1, _password);

// Assert
Assert.AreEqual(_connectionString1, sfSession.ConnectionString);
Assert.AreEqual(_password, sfSession.Password);
}

[Test]
public async Task TestGetSessionAsyncWorksForSpecifiedConnectionString()
{
// Act
var sfSession = await _connectionPoolManager.GetSessionAsync(_connectionString1, _password, CancellationToken.None);

// Assert
Assert.AreEqual(_connectionString1, sfSession.ConnectionString);
Assert.AreEqual(_password, sfSession.Password);
}

[Test]
[Ignore("Enable after completion of SNOW-937189")] // TODO:
public void TestCountingOfSessionProvidedByPool()
{
// Act
_connectionPoolManager.GetSession(_connectionString1, _password);

// Assert
var sessionPool = _connectionPoolManager.GetPool(_connectionString1, _password);
Assert.AreEqual(1, sessionPool.GetCurrentPoolSize());
}

[Test]
[Ignore("Enable after completion of SNOW-937189")] // TODO:
public void TestCountingOfSessionReturnedBackToPool()
{
// Arrange
var sfSession = _connectionPoolManager.GetSession(_connectionString1, _password);

// Act
_connectionPoolManager.AddSession(sfSession);

// Assert
var sessionPool = _connectionPoolManager.GetPool(_connectionString1, _password);
Assert.AreEqual(1, sessionPool.GetCurrentPoolSize());
}

[Test]
public void TestSetMaxPoolSizeForAllPools()
{
// Arrange
var sessionPool1 = _connectionPoolManager.GetPool(_connectionString1, _password);
var sessionPool2 = _connectionPoolManager.GetPool(_connectionString2, _password);

// Act
_connectionPoolManager.SetMaxPoolSize(3);

// Assert
Assert.AreEqual(3, sessionPool1.GetMaxPoolSize());
Assert.AreEqual(3, sessionPool2.GetMaxPoolSize());
}

[Test]
public void TestSetTimeoutForAllPools()
{
// Arrange
var sessionPool1 = _connectionPoolManager.GetPool(_connectionString1, _password);
var sessionPool2 = _connectionPoolManager.GetPool(_connectionString2, _password);

// Act
_connectionPoolManager.SetTimeout(3000);

// Assert
Assert.AreEqual(3000, sessionPool1.GetTimeout());
Assert.AreEqual(3000, sessionPool2.GetTimeout());
}

[Test]
public void TestSetPoolingDisabledForAllPools()
{
// Arrange
var sessionPool1 = _connectionPoolManager.GetPool(_connectionString1, _password);

// Act
_connectionPoolManager.SetPooling(false);

// Assert
Assert.AreEqual(false, sessionPool1.GetPooling());
}

[Test]
public void TestSetPoolingEnabledBack()
{
// Arrange
var sessionPool1 = _connectionPoolManager.GetPool(_connectionString1, _password);
_connectionPoolManager.SetPooling(false);

// Act
_connectionPoolManager.SetPooling(true);

// Assert
Assert.AreEqual(true, sessionPool1.GetPooling());
}

[Test]
public void TestGetPoolingOnManagerLevelNotSupported()
{
Assert.Throws<NotSupportedException>(() => _connectionPoolManager.GetPooling());
}

[Test]
public void TestGetTimeoutOnManagerLevelNotSupported()
{
Assert.Throws<NotSupportedException>(() => _connectionPoolManager.GetTimeout());
}

[Test]
public void TestGetMaxPoolSizeOnManagerLevelNotSupported()
{
Assert.Throws<NotSupportedException>(() => _connectionPoolManager.GetMaxPoolSize());
}
}

class MockSessionFactory : ISessionFactory
{
public SFSession NewSession(string connectionString, SecureString password)
{
var mockSfSession = new Mock<SFSession>(connectionString, password);
mockSfSession.Setup(x => x.Open()).Verifiable();
mockSfSession.Setup(x => x.OpenAsync(default)).Returns(Task.FromResult(this));
return mockSfSession.Object;
}
}

}
4 changes: 4 additions & 0 deletions Snowflake.Data.Tests/Util/PoolConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

using Snowflake.Data.Client;
using Snowflake.Data.Core.Session;

namespace Snowflake.Data.Tests.Util
{
Expand All @@ -11,19 +12,22 @@ 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()
{
SnowflakeDbConnectionPool.SetMaxPoolSize(_maxPoolSize);
SnowflakeDbConnectionPool.SetTimeout(_timeout);
SnowflakeDbConnectionPool.SetPooling(_pooling);
SnowflakeDbConnectionPool.SetConnectionPoolVersion(_connectionPoolType);
}
}
}
84 changes: 70 additions & 14 deletions Snowflake.Data/Client/SnowflakeDbConnectionPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
*/

using System;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -14,72 +15,127 @@ namespace Snowflake.Data.Client
public class SnowflakeDbConnectionPool
{
private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger<SnowflakeDbConnectionPool>();
private static readonly IConnectionManager s_connectionManager = new ConnectionCacheManager();
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;
SetConnectionPoolVersion(s_defaultConnectionPoolType);
return s_connectionManager;
}
}

internal static SFSession GetSession(string connectionString, SecureString password)
{
s_logger.Debug("SnowflakeDbConnectionPool::GetSession");
return s_connectionManager.GetSession(connectionString, password);
s_logger.Debug($"SnowflakeDbConnectionPool::GetSession for {connectionString}");
return ConnectionManager.GetSession(connectionString, password);
}

internal static Task<SFSession> GetSessionAsync(string connectionString, SecureString password, CancellationToken cancellationToken)
{
s_logger.Debug("SnowflakeDbConnectionPool::GetSessionAsync");
return s_connectionManager.GetSessionAsync(connectionString, password, cancellationToken);
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)
{
s_logger.Debug("SnowflakeDbConnectionPool::AddSession");
return s_connectionManager.AddSession(session);
return ConnectionManager.AddSession(session);
}

public static void ClearAllPools()
{
s_logger.Debug("SnowflakeDbConnectionPool::ClearAllPools");
s_connectionManager.ClearAllPools();
ConnectionManager.ClearAllPools();
}

public static void SetMaxPoolSize(int maxPoolSize)
{
s_logger.Debug("SnowflakeDbConnectionPool::SetMaxPoolSize");
s_connectionManager.SetMaxPoolSize(maxPoolSize);
ConnectionManager.SetMaxPoolSize(maxPoolSize);
}

public static int GetMaxPoolSize()
{
s_logger.Debug("SnowflakeDbConnectionPool::GetMaxPoolSize");
return s_connectionManager.GetMaxPoolSize();
return ConnectionManager.GetMaxPoolSize();
}

public static void SetTimeout(long connectionTimeout)
{
s_logger.Debug("SnowflakeDbConnectionPool::SetTimeout");
s_connectionManager.SetTimeout(connectionTimeout);
ConnectionManager.SetTimeout(connectionTimeout);
}

public static long GetTimeout()
{
s_logger.Debug("SnowflakeDbConnectionPool::GetTimeout");
return s_connectionManager.GetTimeout();
return ConnectionManager.GetTimeout();
}

public static int GetCurrentPoolSize()
{
s_logger.Debug("SnowflakeDbConnectionPool::GetCurrentPoolSize");
return s_connectionManager.GetCurrentPoolSize();
return ConnectionManager.GetCurrentPoolSize();
}

public static bool SetPooling(bool isEnable)
{
s_logger.Debug("SnowflakeDbConnectionPool::SetPooling");
return s_connectionManager.SetPooling(isEnable);
return ConnectionManager.SetPooling(isEnable);
}

public static bool GetPooling()
{
s_logger.Debug("SnowflakeDbConnectionPool::GetPooling");
return s_connectionManager.GetPooling();
return ConnectionManager.GetPooling();
}

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)
{
s_connectionManager?.ClearAllPools();
if (ConnectionPoolType.MultipleConnectionPool.Equals(requestedPoolType))
{
s_connectionManager = new ConnectionPoolManager();
s_logger.Info("SnowflakeDbConnectionPool - multiple connection pools enabled");
}
if (ConnectionPoolType.SingleConnectionCache.Equals(requestedPoolType))
{
s_connectionManager = new ConnectionCacheManager();
s_logger.Warn("SnowflakeDbConnectionPool - connection cache enabled");
}
}
}

internal static ConnectionPoolType GetConnectionPoolVersion()
{
if (ConnectionManager != null)
{
switch (ConnectionManager)
{
case ConnectionCacheManager _: return ConnectionPoolType.SingleConnectionCache;
case ConnectionPoolManager _: return ConnectionPoolType.MultipleConnectionPool;
}
}
return s_defaultConnectionPoolType;
}
}
}
Loading

0 comments on commit c4c4164

Please sign in to comment.