Skip to content

Commit

Permalink
SNOW-902608 integration tests for two versions of Connection Pool
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-mhofman committed Oct 16, 2023
1 parent 9d367be commit 4629f01
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 73 deletions.
21 changes: 10 additions & 11 deletions Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolAsyncIT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
222 changes: 189 additions & 33 deletions Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolIT.cs

Large diffs are not rendered by default.

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);
}
}
}
46 changes: 35 additions & 11 deletions Snowflake.Data/Client/SnowflakeDbConnectionPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,36 @@ public class SnowflakeDbConnectionPool
private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger<SnowflakeDbConnectionPool>();
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<SFSession> 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)
{
Expand Down Expand Up @@ -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;
}
}
}
1 change: 1 addition & 0 deletions Snowflake.Data/Core/Session/ConnectionManagerV1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ public Task<SFSession> 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;
}
}
75 changes: 57 additions & 18 deletions Snowflake.Data/Core/Session/ConnectionPoolManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<SFSession> 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();
Expand All @@ -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)
{
Expand All @@ -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)
{
Expand Down
8 changes: 8 additions & 0 deletions Snowflake.Data/Core/Session/ConnectionPoolType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Snowflake.Data.Core.Session
{
internal enum ConnectionPoolType
{
SingleConnectionCache,
MultipleConnectionPool
}
}
1 change: 1 addition & 0 deletions Snowflake.Data/Core/Session/IConnectionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ internal interface IConnectionManager
int GetCurrentPoolSize();
bool SetPooling(bool poolingEnabled);
bool GetPooling();
SessionPool GetPool(string connectionString);
}
}

0 comments on commit 4629f01

Please sign in to comment.