From 5e4ef69d1174d421cb4a0f3318893853ea4030e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Hofman?= Date: Mon, 29 Apr 2024 21:03:02 +0200 Subject: [PATCH] SNOW-937188 introduction of full API for new pool --- .../Client/SnowflakeDbConnection.cs | 30 +++++------ .../Client/SnowflakeDbSessionPool.cs | 17 ++++++- .../Core/Session/ChangedSessionBehavior.cs | 6 +-- .../Core/Session/ConnectionPoolManager.cs | 5 +- Snowflake.Data/Core/Session/SessionPool.cs | 51 +++++++++++++++++-- 5 files changed, 83 insertions(+), 26 deletions(-) diff --git a/Snowflake.Data/Client/SnowflakeDbConnection.cs b/Snowflake.Data/Client/SnowflakeDbConnection.cs index b0592db80..fc0ba199d 100755 --- a/Snowflake.Data/Client/SnowflakeDbConnection.cs +++ b/Snowflake.Data/Client/SnowflakeDbConnection.cs @@ -18,12 +18,12 @@ public class SnowflakeDbConnection : DbConnection { private SFLogger logger = SFLoggerFactory.GetLogger(); - internal SFSession SfSession { get; set; } + internal SFSession SfSession { get; set; } internal ConnectionState _connectionState; protected override DbProviderFactory DbProviderFactory => new SnowflakeDbFactory(); - + internal int _connectionTimeout; private bool _disposed = false; @@ -47,7 +47,7 @@ protected enum TransactionRollbackStatus public SnowflakeDbConnection() { _connectionState = ConnectionState.Closed; - _connectionTimeout = + _connectionTimeout = int.Parse(SFSessionProperty.CONNECTION_TIMEOUT.GetAttribute(). defaultValue); _isArrayBindStageCreated = false; @@ -84,12 +84,12 @@ private bool IsNonClosedWithSession() public override int ConnectionTimeout => this._connectionTimeout; /// - /// If the connection to the database is closed, the DataSource returns whatever is contained - /// in the ConnectionString for the DataSource keyword. If the connection is open and the - /// ConnectionString data source keyword's value starts with "|datadirectory|", the property - /// returns whatever is contained in the ConnectionString for the DataSource keyword only. If - /// the connection to the database is open, the property returns what the native provider - /// returns for the DBPROP_INIT_DATASOURCE, and if that is empty, the native provider's + /// If the connection to the database is closed, the DataSource returns whatever is contained + /// in the ConnectionString for the DataSource keyword. If the connection is open and the + /// ConnectionString data source keyword's value starts with "|datadirectory|", the property + /// returns whatever is contained in the ConnectionString for the DataSource keyword only. If + /// the connection to the database is open, the property returns what the native provider + /// returns for the DBPROP_INIT_DATASOURCE, and if that is empty, the native provider's /// DBPROP_DATASOURCENAME is returned. /// Note: not yet implemented /// @@ -115,7 +115,7 @@ public void PreventPooling() SfSession.SetPooling(false); logger.Debug($"Session {SfSession.sessionId} marked not to be pooled any more"); } - + internal bool HasActiveExplicitTransaction() => ExplicitTransaction != null && ExplicitTransaction.IsActive; private bool TryToReturnSessionToPool() @@ -150,12 +150,12 @@ private TransactionRollbackStatus TerminateTransactionForDirtyConnectionReturnin // error to indicate a problem within application code that a connection was closed while still having a pending transaction logger.Error("Closing dirty connection: rollback transaction in session " + SfSession.sessionId + " succeeded."); ExplicitTransaction = null; - return TransactionRollbackStatus.Success; + return TransactionRollbackStatus.Success; } } catch (Exception exception) { - // error to indicate a problem with rollback of an active transaction and inability to return dirty connection to the pool + // error to indicate a problem with rollback of an active transaction and inability to return dirty connection to the pool logger.Error("Closing dirty connection: rollback transaction in session: " + SfSession.sessionId + " failed, exception: " + exception.Message); return TransactionRollbackStatus.Failure; // connection won't be pooled } @@ -254,10 +254,10 @@ await SfSession.CloseAsync(cancellationToken).ContinueWith( protected virtual bool CanReuseSession(TransactionRollbackStatus transactionRollbackStatus) { - return SnowflakeDbConnectionPool.GetPooling() && + return SnowflakeDbConnectionPool.GetPooling() && transactionRollbackStatus == TransactionRollbackStatus.Success; } - + public override void Open() { logger.Debug("Open Connection."); @@ -401,7 +401,7 @@ protected override void Dispose(bool disposing) SfSession = null; _connectionState = ConnectionState.Closed; } - + _disposed = true; } diff --git a/Snowflake.Data/Client/SnowflakeDbSessionPool.cs b/Snowflake.Data/Client/SnowflakeDbSessionPool.cs index c680ef7f4..1ff9ab972 100644 --- a/Snowflake.Data/Client/SnowflakeDbSessionPool.cs +++ b/Snowflake.Data/Client/SnowflakeDbSessionPool.cs @@ -6,6 +6,7 @@ namespace Snowflake.Data.Client { + public class SnowflakeDbSessionPool : IDisposable { private SessionPool _sessionPool; @@ -17,12 +18,24 @@ internal SnowflakeDbSessionPool(SessionPool sessionPool) public void SetMaxPoolSize(int size) => _sessionPool.SetMaxPoolSize(size); public int GetMaxPoolSize() => _sessionPool.GetMaxPoolSize(); - public void SetTimeout(long seconds) => _sessionPool.SetTimeout(seconds); - public long GetTimeout() => _sessionPool.GetTimeout(); + public void SetMinPoolSize(int size) => _sessionPool.SetMinPoolSize(size); + public int GetMinPoolSize() => _sessionPool.GetMinPoolSize(); + + public void SetExpirationTimeout(long seconds) => _sessionPool.SetTimeout(seconds); + public long GetExpirationTimeout() => _sessionPool.GetTimeout(); + + public void SetConnectionTimeout(long seconds) => _sessionPool.SetTimeout(seconds); + public long GetConnectionTimeout() => _sessionPool.GetTimeout(); public int GetCurrentPoolSize() => _sessionPool.GetCurrentPoolSize(); public bool SetPooling(bool isEnable) => _sessionPool.SetPooling(isEnable); public bool GetPooling() => _sessionPool.GetPooling(); + + public void SetChangedSession(ChangedSessionBehavior newChangedSession) => _sessionPool.SetChangedSession(newChangedSession); + public ChangedSessionBehavior GetChangedSession() => _sessionPool.GetChangedSession(); + + public void SetWaitForIdleSessionTimeout(double seconds) => _sessionPool.SetWaitForIdleSessionTimeout(seconds); + public double GetWaitForIdleSessionTimeout() => _sessionPool.GetWaitForIdleSessionTimeout(); } } diff --git a/Snowflake.Data/Core/Session/ChangedSessionBehavior.cs b/Snowflake.Data/Core/Session/ChangedSessionBehavior.cs index 50cf9893c..50cb6dd78 100644 --- a/Snowflake.Data/Core/Session/ChangedSessionBehavior.cs +++ b/Snowflake.Data/Core/Session/ChangedSessionBehavior.cs @@ -5,9 +5,9 @@ namespace Snowflake.Data.Core.Session { /** - * It describes what should happen to a session with a changed state (e. g. schema/role/etc.) when it is being returned to the pool. + * ChangedSessionBehavior describes what should happen to a session with a changed state (schema/role/database/warehouse) when it is being returned to the pool. */ - internal enum ChangedSessionBehavior + public enum ChangedSessionBehavior { OriginalPool, ChangePool, @@ -24,7 +24,7 @@ public static List StringValues() .Select(b => b.ToString()) .ToList(); } - + public static ChangedSessionBehavior From(string changedSession) { return (ChangedSessionBehavior) Enum.Parse(typeof(ChangedSessionBehavior), changedSession, true); diff --git a/Snowflake.Data/Core/Session/ConnectionPoolManager.cs b/Snowflake.Data/Core/Session/ConnectionPoolManager.cs index dfc9b218d..ccd20326a 100644 --- a/Snowflake.Data/Core/Session/ConnectionPoolManager.cs +++ b/Snowflake.Data/Core/Session/ConnectionPoolManager.cs @@ -140,7 +140,9 @@ public SessionPool GetPool(string connectionString, SecureString password) public SessionPool GetPool(string connectionString) { s_logger.Debug($"ConnectionPoolManager::GetPool"); - if (!connectionString.ToLower().Contains("password=")) + // unless it is an external browser then those two must be passed along + var connStr = $";{connectionString.ToLower()};"; + if (!connStr.Contains(";password=") && connStr.Contains(";user=")) { s_logger.Error($"To obtain a pool a password must to be given with a connection string or SecureString parameter"); throw new SnowflakeDbException(SFError.MISSING_CONNECTION_PROPERTY, "Could not provide the pool without the password"); @@ -148,7 +150,6 @@ public SessionPool GetPool(string connectionString) return GetPool(connectionString, null); } - // TODO: SNOW-937188 private string GetPoolKey(string connectionString) { return connectionString; diff --git a/Snowflake.Data/Core/Session/SessionPool.cs b/Snowflake.Data/Core/Session/SessionPool.cs index e2322c7d8..bd2c5a90b 100644 --- a/Snowflake.Data/Core/Session/SessionPool.cs +++ b/Snowflake.Data/Core/Session/SessionPool.cs @@ -521,15 +521,58 @@ internal async void ClearAllPoolsAsync() } } - public void SetMaxPoolSize(int size) + public void SetMaxPoolSize(int newMaxPoolSize) { - _poolConfig.MaxPoolSize = size; + s_logger.Info($"SessionPool::SetMaxPoolSize({newMaxPoolSize})"); + _poolConfig.MaxPoolSize = newMaxPoolSize; _configOverriden = true; } - public int GetMaxPoolSize() + public int GetMaxPoolSize() => _poolConfig.MaxPoolSize; + + public void SetMinPoolSize(int newMinPoolSize) + { + s_logger.Info($"SessionPool::SetMinPoolSize({newMinPoolSize})"); + _poolConfig.MinPoolSize = newMinPoolSize; + _configOverriden = true; + } + + public int GetMinPoolSize() => _poolConfig.MinPoolSize; + + public ChangedSessionBehavior GetChangedSession() => _poolConfig.ChangedSession; + + public void SetChangedSession(ChangedSessionBehavior newChangedSession) + { + if (IsMultiplePoolsVersion()) + { + s_logger.Info($"SessionPool::SetChangedSession({newChangedSession})"); + _poolConfig.ChangedSession = newChangedSession; + _configOverriden = true; + } + } + + public double GetWaitForIdleSessionTimeout() => _poolConfig.WaitingForIdleSessionTimeout.TotalSeconds; + + public void SetWaitForIdleSessionTimeout(double waitForIdleSession) + { + if (IsMultiplePoolsVersion()) + { + s_logger.Info($"SessionPool::SetWaitForIdleSessionTimeout({waitForIdleSession})"); + _poolConfig.WaitingForIdleSessionTimeout = TimeSpan.FromSeconds(waitForIdleSession); + _configOverriden = true; + } + } + + public void SetConnectionTimeout(long seconds) + { + var timeout = seconds < 0 ? TimeoutHelper.Infinity() : TimeSpan.FromSeconds(seconds); + _poolConfig.ConnectionTimeout = timeout; + _configOverriden = true; + } + + public long GetConnectionTimeout() { - return _poolConfig.MaxPoolSize; + return TimeoutHelper.IsInfinite(_poolConfig.ConnectionTimeout) ? -1 : (long)_poolConfig.ConnectionTimeout.TotalSeconds; } public void SetTimeout(long seconds)