Skip to content

Commit

Permalink
SNOW-937190 max pool size reached with wait support and timeout excep…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
sfc-gh-mhofman committed Oct 11, 2023
1 parent 04914ff commit 4e0bd12
Showing 1 changed file with 48 additions and 2 deletions.
50 changes: 48 additions & 2 deletions Snowflake.Data/Core/Session/SessionPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ sealed class SessionPool : IDisposable
private SecureString _password;
private bool _pooling = true;
private int _busySessions;
private bool _allowExceedMaxPoolSize = true;
private bool _allowExceedMaxPoolSize = true; // differentiates behavior when max size reached
private int _waitForIdleSessionSleepTimeout = 5;
private int _waitForIdleSessionTimeout = 120;

internal SessionPool()
{
Expand All @@ -43,7 +45,7 @@ internal SessionPool(string connectionString, SecureString password) : this()
{
_connectionString = connectionString;
_password = password;
_allowExceedMaxPoolSize = false; // TODO: SNOW-937190
_allowExceedMaxPoolSize = false;

Check warning on line 48 in Snowflake.Data/Core/Session/SessionPool.cs

View check run for this annotation

Codecov / codecov/patch

Snowflake.Data/Core/Session/SessionPool.cs#L46-L48

Added lines #L46 - L48 were not covered by tests
}

internal static SessionPool CreateSessionPoolV1() => new SessionPool();
Expand Down Expand Up @@ -133,6 +135,11 @@ private SFSession GetIdleSession(string connStr)
private SFSession NewSession(String connectionString, SecureString password)
{
s_logger.Debug("SessionPool::NewSession");
if (!_allowExceedMaxPoolSize && GetCurrentPoolSize() >= MaxPoolSize)
{
s_logger.Warn("Max pool size reached");
return WaitForPooledSession(CancellationToken.None);

Check warning on line 141 in Snowflake.Data/Core/Session/SessionPool.cs

View check run for this annotation

Codecov / codecov/patch

Snowflake.Data/Core/Session/SessionPool.cs#L139-L141

Added lines #L139 - L141 were not covered by tests
}
try
{
var session = new SFSession(connectionString, password);
Expand All @@ -156,6 +163,11 @@ private SFSession NewSession(String connectionString, SecureString password)
private Task<SFSession> NewSessionAsync(String connectionString, SecureString password, CancellationToken cancellationToken)
{
s_logger.Debug("SessionPool::NewSessionAsync");
if (!_allowExceedMaxPoolSize && GetCurrentPoolSize() >= MaxPoolSize)
{
s_logger.Warn("Max pool size reached");
return Task.Run(() => WaitForPooledSession(cancellationToken), cancellationToken);

Check warning on line 169 in Snowflake.Data/Core/Session/SessionPool.cs

View check run for this annotation

Codecov / codecov/patch

Snowflake.Data/Core/Session/SessionPool.cs#L167-L169

Added lines #L167 - L169 were not covered by tests
}
var session = new SFSession(connectionString, password);
_busySessions++;
return session
Expand All @@ -174,12 +186,46 @@ private Task<SFSession> NewSessionAsync(String connectionString, SecureString pa
return session;
}, TaskContinuationOptions.NotOnCanceled);
}

private SFSession WaitForPooledSession(CancellationToken cancellationToken)
{
if (!_pooling)
return null;

Check warning on line 193 in Snowflake.Data/Core/Session/SessionPool.cs

View check run for this annotation

Codecov / codecov/patch

Snowflake.Data/Core/Session/SessionPool.cs#L191-L193

Added lines #L191 - L193 were not covered by tests

long start = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
while (true)
{
if (cancellationToken.IsCancellationRequested)
cancellationToken.ThrowIfCancellationRequested();

Check warning on line 199 in Snowflake.Data/Core/Session/SessionPool.cs

View check run for this annotation

Codecov / codecov/patch

Snowflake.Data/Core/Session/SessionPool.cs#L195-L199

Added lines #L195 - L199 were not covered by tests

lock (s_sessionPoolLock)
{
var session = GetIdleSession(_connectionString);
if (session != null)
{
_idleSessions.Remove(session);
_busySessions++;
return session;

Check warning on line 208 in Snowflake.Data/Core/Session/SessionPool.cs

View check run for this annotation

Codecov / codecov/patch

Snowflake.Data/Core/Session/SessionPool.cs#L201-L208

Added lines #L201 - L208 were not covered by tests
}
}

Check warning on line 210 in Snowflake.Data/Core/Session/SessionPool.cs

View check run for this annotation

Codecov / codecov/patch

Snowflake.Data/Core/Session/SessionPool.cs#L210

Added line #L210 was not covered by tests

Thread.Sleep(_waitForIdleSessionSleepTimeout);
long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
if (start + _waitForIdleSessionTimeout < now)
throw new SnowflakeDbException(
new TimeoutException("No free connections in the pool."), // TODO:
SnowflakeDbException.CONNECTION_FAILURE_SSTATE,
SFError.REQUEST_TIMEOUT,
"Unable to connect.");
}
}

Check warning on line 221 in Snowflake.Data/Core/Session/SessionPool.cs

View check run for this annotation

Codecov / codecov/patch

Snowflake.Data/Core/Session/SessionPool.cs#L212-L221

Added lines #L212 - L221 were not covered by tests

internal bool AddSession(SFSession session)
{
s_logger.Debug("SessionPool::AddSession");
if (!_pooling)
return false;

long timeNow = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
if (session.IsNotOpen() || session.IsExpired(_timeout, timeNow))
{
Expand Down

0 comments on commit 4e0bd12

Please sign in to comment.