diff --git a/Snowflake.Data.Tests/IntegrationTests/ConnectionMultiplePoolsIT.cs b/Snowflake.Data.Tests/IntegrationTests/ConnectionMultiplePoolsIT.cs index c5ff91639..bfab3f2a6 100644 --- a/Snowflake.Data.Tests/IntegrationTests/ConnectionMultiplePoolsIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/ConnectionMultiplePoolsIT.cs @@ -1,3 +1,4 @@ +using System; using System.Data; using NUnit.Framework; using Snowflake.Data.Client; @@ -33,7 +34,28 @@ public static void AfterAllTests() } [Test] - public void TestNewConnectionPoolFull() + public void TestReuseSessionInConnectionPool() // old name: TestConnectionPool + { + var conn1 = new SnowflakeDbConnection(ConnectionString); + conn1.Open(); + Assert.AreEqual(ConnectionState.Open, conn1.State); + conn1.Close(); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); + + var conn2 = new SnowflakeDbConnection(); + conn2.ConnectionString = ConnectionString; + conn2.Open(); + Assert.AreEqual(ConnectionState.Open, conn2.State); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); + + conn2.Close(); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); + Assert.AreEqual(ConnectionState.Closed, conn1.State); + Assert.AreEqual(ConnectionState.Closed, conn2.State); + } + + [Test] + public void TestReuseSessionInConnectionPoolReachingMaxConnections() // old name: TestConnectionPoolFull { var pool = SnowflakeDbConnectionPool.GetPool(ConnectionString); pool.SetMaxPoolSize(2); @@ -48,7 +70,7 @@ public void TestNewConnectionPoolFull() conn2.Open(); Assert.AreEqual(ConnectionState.Open, conn2.State); - Assert.AreEqual(0, pool.GetCurrentPoolSize()); + Assert.AreEqual(2, pool.GetCurrentPoolSize()); conn1.Close(); conn2.Close(); Assert.AreEqual(2, pool.GetCurrentPoolSize()); @@ -64,7 +86,7 @@ public void TestNewConnectionPoolFull() Assert.AreEqual(ConnectionState.Open, conn4.State); conn3.Close(); - Assert.AreEqual(1, pool.GetCurrentPoolSize()); // TODO: when SNOW-937189 complete should be 2 + Assert.AreEqual(2, pool.GetCurrentPoolSize()); conn4.Close(); Assert.AreEqual(2, pool.GetCurrentPoolSize()); @@ -74,6 +96,63 @@ public void TestNewConnectionPoolFull() Assert.AreEqual(ConnectionState.Closed, conn4.State); } + [Test] + public void TestBusyAndIdleConnectionsCountedInPoolSize() + { + // arrange + var pool = SnowflakeDbConnectionPool.GetPool(ConnectionString); + pool.SetMaxPoolSize(2); + var connection = new SnowflakeDbConnection(); + connection.ConnectionString = ConnectionString; + + // act + connection.Open(); + + // assert + Assert.AreEqual(1, pool.GetCurrentPoolSize()); + + // act + connection.Close(); + + // assert + Assert.AreEqual(1, pool.GetCurrentPoolSize()); + } + + [Test] + [Ignore("Enable when disabling pooling in connection string enabled - SNOW-902632")] + public void TestConnectionPoolNotPossibleToDisableForAllPools() + { + // act + var thrown = Assert.Throws(() => SnowflakeDbConnectionPool.SetPooling(false)); + + // assert + Assert.IsNotNull(thrown); + } + + [Test] + public void TestConnectionPoolDisable() + { + // arrange + var pool = SnowflakeDbConnectionPool.GetPool(ConnectionString); + pool.SetPooling(false); + var conn1 = new SnowflakeDbConnection(); + conn1.ConnectionString = ConnectionString; + + // act + conn1.Open(); + + // assert + Assert.AreEqual(ConnectionState.Open, conn1.State); + Assert.AreEqual(0, pool.GetCurrentPoolSize()); + + // act + conn1.Close(); + + // assert + Assert.AreEqual(ConnectionState.Closed, conn1.State); + Assert.AreEqual(0, pool.GetCurrentPoolSize()); + } + [Test] public void TestNewConnectionPoolClean() { diff --git a/Snowflake.Data.Tests/IntegrationTests/ConnectionPoolCommonIT.cs b/Snowflake.Data.Tests/IntegrationTests/ConnectionPoolCommonIT.cs index ac9c085ee..7129e5f5f 100644 --- a/Snowflake.Data.Tests/IntegrationTests/ConnectionPoolCommonIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/ConnectionPoolCommonIT.cs @@ -150,27 +150,6 @@ public void TestBasicConnectionPool() Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); } - [Test] - public void TestConnectionPool() - { - var conn1 = new SnowflakeDbConnection(ConnectionString); - conn1.Open(); - Assert.AreEqual(ConnectionState.Open, conn1.State); - conn1.Close(); - 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.GetPool(ConnectionString).GetCurrentPoolSize()); - - conn2.Close(); - Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); - Assert.AreEqual(ConnectionState.Closed, conn1.State); - Assert.AreEqual(ConnectionState.Closed, conn2.State); - } - [Test] public void TestConnectionPoolIsFull() { @@ -273,25 +252,9 @@ void ThreadProcess2(string connstr) conn1.Close(); SnowflakeDbConnectionPool.ClearAllPools(); SnowflakeDbConnectionPool.SetMaxPoolSize(0); - SnowflakeDbConnectionPool.SetPooling(false); - } - - [Test] - public void TestConnectionPoolDisable() - { - var pool = SnowflakeDbConnectionPool.GetPool(ConnectionString); - pool.SetPooling(false); - - var conn1 = new SnowflakeDbConnection(); - conn1.ConnectionString = ConnectionString; - conn1.Open(); - Assert.AreEqual(ConnectionState.Open, conn1.State); - conn1.Close(); - - Assert.AreEqual(ConnectionState.Closed, conn1.State); - Assert.AreEqual(0, pool.GetCurrentPoolSize()); + SnowflakeDbConnectionPool.SetPooling(true); } - + [Test] public void TestConnectionPoolWithDispose() { @@ -305,22 +268,5 @@ public void TestConnectionPoolWithDispose() Assert.AreEqual(ConnectionState.Closed, conn1.State); Assert.AreEqual(0, SnowflakeDbConnectionPool.GetPool(conn1.ConnectionString).GetCurrentPoolSize()); } - - [Test] - public void TestConnectionPoolTurnOff() - { - SnowflakeDbConnectionPool.SetPooling(false); - SnowflakeDbConnectionPool.SetPooling(true); - SnowflakeDbConnectionPool.SetMaxPoolSize(1); - - var conn1 = new SnowflakeDbConnection(); - conn1.ConnectionString = ConnectionString; - conn1.Open(); - Assert.AreEqual(ConnectionState.Open, conn1.State); - conn1.Close(); - - Assert.AreEqual(ConnectionState.Closed, conn1.State); - Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); - } } } diff --git a/Snowflake.Data.Tests/IntegrationTests/ConnectionSinglePoolCacheIT.cs b/Snowflake.Data.Tests/IntegrationTests/ConnectionSinglePoolCacheIT.cs index 42365d926..5e0e278c1 100644 --- a/Snowflake.Data.Tests/IntegrationTests/ConnectionSinglePoolCacheIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/ConnectionSinglePoolCacheIT.cs @@ -33,7 +33,28 @@ public static void AfterAllTests() } [Test] - public void TestConnectionPoolFull() + public void TestPoolContainsClosedConnections() // old name: TestConnectionPool + { + var conn1 = new SnowflakeDbConnection(ConnectionString); + conn1.Open(); + Assert.AreEqual(ConnectionState.Open, conn1.State); + conn1.Close(); + 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.GetPool(ConnectionString).GetCurrentPoolSize()); + + conn2.Close(); + Assert.AreEqual(1, SnowflakeDbConnectionPool.GetPool(ConnectionString).GetCurrentPoolSize()); + Assert.AreEqual(ConnectionState.Closed, conn1.State); + Assert.AreEqual(ConnectionState.Closed, conn2.State); + } + + [Test] + public void TestPoolContainsAtMostMaxPoolSizeConnections() // old name: TestConnectionPoolFull { SnowflakeDbConnectionPool.SetMaxPoolSize(2); @@ -71,7 +92,54 @@ public void TestConnectionPoolFull() Assert.AreEqual(ConnectionState.Closed, conn4.State); SnowflakeDbConnectionPool.ClearAllPools(); } + + [Test] + public void TestConnectionPoolDisableFromPoolManagerLevel() + { + // arrange + SnowflakeDbConnectionPool.SetPooling(false); + var conn1 = new SnowflakeDbConnection(); + conn1.ConnectionString = ConnectionString; + + // act + conn1.Open(); + + // assert + Assert.AreEqual(ConnectionState.Open, conn1.State); + Assert.AreEqual(0, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + + // act + conn1.Close(); + + // assert + Assert.AreEqual(ConnectionState.Closed, conn1.State); + Assert.AreEqual(0, SnowflakeDbConnectionPool.GetCurrentPoolSize()); + } + [Test] + public void TestConnectionPoolDisable() + { + // arrange + var pool = SnowflakeDbConnectionPool.GetPool(ConnectionString); + pool.SetPooling(false); + var conn1 = new SnowflakeDbConnection(); + conn1.ConnectionString = ConnectionString; + + // act + conn1.Open(); + + // assert + Assert.AreEqual(ConnectionState.Open, conn1.State); + Assert.AreEqual(0, pool.GetCurrentPoolSize()); + + // act + conn1.Close(); + + // assert + Assert.AreEqual(ConnectionState.Closed, conn1.State); + Assert.AreEqual(0, pool.GetCurrentPoolSize()); + } + [Test] public void TestConnectionPoolClean() { diff --git a/Snowflake.Data.Tests/UnitTests/ConnectionPoolManagerTest.cs b/Snowflake.Data.Tests/UnitTests/ConnectionPoolManagerTest.cs index a28a3754c..ebcb85c5c 100644 --- a/Snowflake.Data.Tests/UnitTests/ConnectionPoolManagerTest.cs +++ b/Snowflake.Data.Tests/UnitTests/ConnectionPoolManagerTest.cs @@ -2,6 +2,7 @@ * Copyright (c) 2023 Snowflake Computing Inc. All rights reserved. */ +using System; using System.Collections.Generic; using System.Security; using System.Threading; @@ -160,30 +161,17 @@ public void TestSetTimeoutForAllPools() } [Test] - public void TestSetPoolingDisabledForAllPools() + [Ignore("Enable when disabling pooling in connection string enabled - SNOW-902632")] + public void TestSetPoolingDisabledForAllPoolsNotPossible() { // Arrange - var sessionPool1 = _connectionPoolManager.GetPool(ConnectionString1, _password); + _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); + var thrown = Assert.Throws(() => _connectionPoolManager.SetPooling(false)); // Assert - Assert.AreEqual(true, sessionPool1.GetPooling()); + Assert.IsNotNull(thrown); } [Test] diff --git a/Snowflake.Data.Tests/UnitTests/Session/FixedZeroCounterTest.cs b/Snowflake.Data.Tests/UnitTests/Session/FixedZeroCounterTest.cs new file mode 100644 index 000000000..79653ef63 --- /dev/null +++ b/Snowflake.Data.Tests/UnitTests/Session/FixedZeroCounterTest.cs @@ -0,0 +1,48 @@ +using NUnit.Framework; +using Snowflake.Data.Core.Session; + +namespace Snowflake.Data.Tests.UnitTests.Session +{ + [TestFixture] + public class FixedZeroCounterTest + { + [Test] + public void TestInitialZero() + { + // arrange + var counter = new FixedZeroCounter(); + + // act + var count = counter.Count(); + + // assert + Assert.AreEqual(0, count); + } + + [Test] + public void TestZeroAfterIncrease() + { + // arrange + var counter = new FixedZeroCounter(); + + // act + counter.Increase(); + + // assert + Assert.AreEqual(0, counter.Count()); + } + + [Test] + public void TestZeroAfterDecrease() + { + // arrange + var counter = new FixedZeroCounter(); + + // act + counter.Decrease(); + + // assert + Assert.AreEqual(0, counter.Count()); + } + } +} diff --git a/Snowflake.Data.Tests/UnitTests/Session/NonNegativeCounterTest.cs b/Snowflake.Data.Tests/UnitTests/Session/NonNegativeCounterTest.cs new file mode 100644 index 000000000..7b63126f4 --- /dev/null +++ b/Snowflake.Data.Tests/UnitTests/Session/NonNegativeCounterTest.cs @@ -0,0 +1,76 @@ +using NUnit.Framework; +using Snowflake.Data.Core.Session; + +namespace Snowflake.Data.Tests.UnitTests.Session +{ + [TestFixture] + public class NonNegativeCounterTest + { + [Test] + public void TestInitialZero() + { + // arrange + var counter = new NonNegativeCounter(); + + // act + var count = counter.Count(); + + // assert + Assert.AreEqual(0, count); + } + + [Test] + public void TestIncrease() + { + // arrange + var counter = new NonNegativeCounter(); + + // act + counter.Increase(); + + // assert + Assert.AreEqual(1, counter.Count()); + + // act + counter.Increase(); + + // assert + Assert.AreEqual(2, counter.Count()); + } + + + [Test] + public void TestDecrease() + { + // arrange + var counter = new NonNegativeCounter(); + counter.Increase(); + counter.Increase(); + + // act + counter.Decrease(); + + // assert + Assert.AreEqual(1, counter.Count()); + + // act + counter.Decrease(); + + // assert + Assert.AreEqual(0, counter.Count()); + } + + [Test] + public void TestDecreaseDoesNotGoBelowZero() + { + // arrange + var counter = new NonNegativeCounter(); + + // act + counter.Decrease(); + + // assert + Assert.AreEqual(0, counter.Count()); + } + } +} diff --git a/Snowflake.Data/Core/Session/ConnectionCacheManager.cs b/Snowflake.Data/Core/Session/ConnectionCacheManager.cs index c871c24f1..4ab1cc3cb 100644 --- a/Snowflake.Data/Core/Session/ConnectionCacheManager.cs +++ b/Snowflake.Data/Core/Session/ConnectionCacheManager.cs @@ -15,7 +15,7 @@ internal sealed class ConnectionCacheManager : IConnectionManager public Task GetSessionAsync(string connectionString, SecureString password, CancellationToken cancellationToken) => _sessionPool.GetSessionAsync(connectionString, password, cancellationToken); public bool AddSession(SFSession session) => _sessionPool.AddSession(session); - public void ClearAllPools() => _sessionPool.ClearAllPools(); + public void ClearAllPools() => _sessionPool.ClearIdleSessions(); public void SetMaxPoolSize(int maxPoolSize) => _sessionPool.SetMaxPoolSize(maxPoolSize); public int GetMaxPoolSize() => _sessionPool.GetMaxPoolSize(); public void SetTimeout(long connectionTimeout) => _sessionPool.SetTimeout(connectionTimeout); diff --git a/Snowflake.Data/Core/Session/ConnectionPoolManager.cs b/Snowflake.Data/Core/Session/ConnectionPoolManager.cs index 844daa54d..a6d9c7294 100644 --- a/Snowflake.Data/Core/Session/ConnectionPoolManager.cs +++ b/Snowflake.Data/Core/Session/ConnectionPoolManager.cs @@ -50,7 +50,7 @@ public void ClearAllPools() s_logger.Debug("ConnectionPoolManager::ClearAllPools"); foreach (var sessionPool in _pools.Values) { - sessionPool.ClearAllPools(); + sessionPool.ClearIdleSessions(); } _pools.Clear(); } @@ -102,6 +102,9 @@ public int GetCurrentPoolSize() public bool SetPooling(bool poolingEnabled) { + // if (!poolingEnabled) // TODO: enable when disabling pooling in connection string completed SNOW-902632 + // throw new Exception( + // "Could not disable pooling for all connections. You could disable pooling by given connection string instead."); s_logger.Debug("ConnectionPoolManager::SetPooling for all pools"); return _pools.Values .Select(pool => pool.SetPooling(poolingEnabled)) diff --git a/Snowflake.Data/Core/Session/FixedZeroCounter.cs b/Snowflake.Data/Core/Session/FixedZeroCounter.cs new file mode 100644 index 000000000..9f545f2f3 --- /dev/null +++ b/Snowflake.Data/Core/Session/FixedZeroCounter.cs @@ -0,0 +1,15 @@ +namespace Snowflake.Data.Core.Session +{ + internal class FixedZeroCounter: ICounter + { + public int Count() => 0; + + public void Increase() + { + } + + public void Decrease() + { + } + } +} diff --git a/Snowflake.Data/Core/Session/ICounter.cs b/Snowflake.Data/Core/Session/ICounter.cs new file mode 100644 index 000000000..61065d73f --- /dev/null +++ b/Snowflake.Data/Core/Session/ICounter.cs @@ -0,0 +1,11 @@ +namespace Snowflake.Data.Core.Session +{ + internal interface ICounter + { + int Count(); + + void Increase(); + + void Decrease(); + } +} diff --git a/Snowflake.Data/Core/Session/NonNegativeCounter.cs b/Snowflake.Data/Core/Session/NonNegativeCounter.cs new file mode 100644 index 000000000..3d755c6cb --- /dev/null +++ b/Snowflake.Data/Core/Session/NonNegativeCounter.cs @@ -0,0 +1,18 @@ +using System; + +namespace Snowflake.Data.Core.Session +{ + internal class NonNegativeCounter : ICounter + { + private int _value; + + public int Count() => _value; + + public void Increase() => _value++; + + public void Decrease() + { + _value = Math.Max(_value - 1, 0); + } + } +} diff --git a/Snowflake.Data/Core/Session/SessionPool.cs b/Snowflake.Data/Core/Session/SessionPool.cs index 0c62f61be..ee30664b1 100644 --- a/Snowflake.Data/Core/Session/SessionPool.cs +++ b/Snowflake.Data/Core/Session/SessionPool.cs @@ -16,7 +16,7 @@ namespace Snowflake.Data.Core.Session sealed class SessionPool : IDisposable { private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger(); - private static readonly object s_sessionPoolLock = new object(); + private readonly object _sessionPoolLock = new object(); private static ISessionFactory s_sessionFactory = new SessionFactory(); private readonly List _idleSessions; @@ -27,20 +27,25 @@ sealed class SessionPool : IDisposable internal string ConnectionString { get; } internal SecureString Password { get; } private bool _pooling = true; - private bool _allowExceedMaxPoolSize = true; + private readonly ICounter _busySessionsCounter; + private readonly bool _allowExceedMaxPoolSize = true; private SessionPool() { - lock (s_sessionPoolLock) - { - _idleSessions = new List(); - _maxPoolSize = MaxPoolSize; - _timeout = Timeout; - } + // acquiring a lock not needed because one is already acquired in SnowflakeDbConnectionPool + _idleSessions = new List(); + _maxPoolSize = MaxPoolSize; + _timeout = Timeout; + _busySessionsCounter = new FixedZeroCounter(); } - private SessionPool(string connectionString, SecureString password) : this() + private SessionPool(string connectionString, SecureString password) { + // acquiring a lock not needed because one is already acquired in ConnectionPoolManager + _idleSessions = new List(); + _maxPoolSize = MaxPoolSize; + _timeout = Timeout; + _busySessionsCounter = new NonNegativeCounter(); ConnectionString = connectionString; Password = password; _allowExceedMaxPoolSize = false; // TODO: SNOW-937190 @@ -60,7 +65,7 @@ internal static SessionPool CreateSessionPool(string connectionString, SecureStr public void Dispose() { - ClearAllPools(); + ClearIdleSessions(); } internal static ISessionFactory SessionFactory @@ -71,7 +76,7 @@ internal static ISessionFactory SessionFactory private void CleanExpiredSessions() { s_logger.Debug("SessionPool::CleanExpiredSessions"); - lock (s_sessionPoolLock) + lock (_sessionPoolLock) { long timeNow = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); @@ -112,7 +117,7 @@ internal Task GetSessionAsync(CancellationToken cancellationToken) => private SFSession GetIdleSession(string connStr) { s_logger.Debug("SessionPool::GetIdleSession"); - lock (s_sessionPoolLock) + lock (_sessionPoolLock) { for (int i = 0; i < _idleSessions.Count; i++) { @@ -129,6 +134,7 @@ private SFSession GetIdleSession(string connStr) else { s_logger.Debug($"reuse pooled session with sid {session.sessionId}"); + _busySessionsCounter.Increase(); return session; } } @@ -144,6 +150,8 @@ private SFSession NewSession(String connectionString, SecureString password) { var session = s_sessionFactory.NewSession(connectionString, password); session.Open(); + if (_pooling) + _busySessionsCounter.Increase(); return session; } catch (Exception e) @@ -176,6 +184,9 @@ private Task NewSessionAsync(String connectionString, SecureString pa SFError.INTERNAL_ERROR, "Failure while opening session async"); + if (_pooling) + _busySessionsCounter.Increase(); + return session; }, TaskContinuationOptions.NotOnCanceled); } @@ -187,15 +198,19 @@ internal bool AddSession(SFSession session) return false; long timeNow = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); if (session.IsNotOpen() || session.IsExpired(_timeout, timeNow)) - return false; - - lock (s_sessionPoolLock) { - if (_idleSessions.Count >= _maxPoolSize) + lock (_sessionPoolLock) { - CleanExpiredSessions(); + _busySessionsCounter.Decrease(); } - if (_idleSessions.Count >= _maxPoolSize) + return false; + } + + lock (_sessionPoolLock) + { + _busySessionsCounter.Decrease(); + CleanExpiredSessions(); + if (GetCurrentPoolSize() >= _maxPoolSize) { s_logger.Warn($"Pool is full - unable to add session with sid {session.sessionId}"); return false; @@ -207,10 +222,10 @@ internal bool AddSession(SFSession session) } } - internal void ClearAllPools() + internal void ClearIdleSessions() { - s_logger.Debug("SessionPool::ClearAllPools"); - lock (s_sessionPoolLock) + s_logger.Debug("SessionPool::ClearIdleSessions"); + lock (_sessionPoolLock) { foreach (SFSession session in _idleSessions) { @@ -252,7 +267,7 @@ public long GetTimeout() public int GetCurrentPoolSize() { - return _idleSessions.Count; + return _idleSessions.Count + _busySessionsCounter.Count(); } public bool SetPooling(bool isEnable) @@ -263,7 +278,7 @@ public bool SetPooling(bool isEnable) _pooling = isEnable; if (!_pooling) { - ClearAllPools(); + ClearIdleSessions(); } return true; }