Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private static YdbCommand CreateCommand(
}


private static YdbConnection CreateConnection() => new(new YdbConnectionStringBuilder { MaxSessionPool = 10 });
private static YdbConnection CreateConnection() => new(new YdbConnectionStringBuilder { MaxPoolSize = 10 });

public override async Task CleanAsync(DbContext context)
{
Expand Down
7 changes: 5 additions & 2 deletions src/Ydb.Sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
- **Breaking Change**: Renamed properties in `YdbConnectionStringBuilder`:
- `MaxSessionPool` -> `MaxPoolSize`.
- `MinSessionPool` -> `MinPoolSize`.
- Added XML documentation for all public APIs in `Ydb.Sdk`.
- Feat ADO.NET: Added dispose timeout (10 seconds) to `PoolingSessionSource`.
- Feat ADO.NET: Added `EnableImplicitSession` to support implicit sessions.
Expand Down Expand Up @@ -43,8 +46,8 @@
- Fixed bug: Grpc.Core.StatusCode.Cancelled was mapped to server's Canceled status.
- Feat ADO.NET: PoolingSessionSource 2.0 based on lock-free FIFO pooling algorithm.
- Added new ADO.NET options:
- `MinSessionPool`: The minimum connection pool size.
- `SessionIdleTimeout`: The time (in seconds) to wait before closing idle session in the pool if the count of all sessions exceeds `MinSessionPool`.
- `MinPoolSize`: The minimum session pool size.
- `SessionIdleTimeout`: The time (in seconds) to wait before closing idle session in the pool if the count of all sessions exceeds `MinPoolSize`.
- Fixed bug `Reader`: unhandled exception in `TryReadRequestBytes(long bytes)`.
- Handle `YdbException` on `DeleteSession`.
- Do not invoke `DeleteSession` if the session is not active.
Expand Down
34 changes: 17 additions & 17 deletions src/Ydb.Sdk/src/Ado/Session/PoolingSessionSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ internal sealed class PoolingSessionSource<T> : ISessionSource where T : Pooling
private readonly CancellationTokenSource _disposeCts = new();

private readonly IPoolingSessionFactory<T> _sessionFactory;
private readonly int _minSessionSize;
private readonly int _maxSessionSize;
private readonly int _minSizePool;
private readonly int _maxSizePool;
private readonly T?[] _sessions;
private readonly int _createSessionTimeout;
private readonly TimeSpan _sessionIdleTimeout;
Expand All @@ -32,16 +32,16 @@ internal sealed class PoolingSessionSource<T> : ISessionSource where T : Pooling
public PoolingSessionSource(IPoolingSessionFactory<T> sessionFactory, YdbConnectionStringBuilder settings)
{
_sessionFactory = sessionFactory;
_minSessionSize = settings.MinSessionPool;
_maxSessionSize = settings.MaxSessionPool;
_minSizePool = settings.MinPoolSize;
_maxSizePool = settings.MaxPoolSize;

if (_minSessionSize > _maxSessionSize)
if (_minSizePool > _maxSizePool)
{
throw new ArgumentException(
$"Connection can't have 'Max Session Pool' {_maxSessionSize} under 'Min Session Pool' {_minSessionSize}");
$"Connection can't have 'Max Session Pool' {_maxSizePool} under 'Min Session Pool' {_minSizePool}");
}

_sessions = new T?[_maxSessionSize];
_sessions = new T?[_maxSizePool];
_createSessionTimeout = settings.CreateSessionTimeout;
_sessionIdleTimeout = TimeSpan.FromSeconds(settings.SessionIdleTimeout);
_cleanerTimer = new Timer(CleanIdleSessions, this, _sessionIdleTimeout, _sessionIdleTimeout);
Expand Down Expand Up @@ -129,8 +129,8 @@ private async ValueTask<ISession> RentAsync(CancellationToken cancellationToken)
}

await using var _ = finalToken.Register(() => waiterTcs.TrySetException(
new YdbException($"The connection pool has been exhausted, either raise 'MaxSessionPool' " +
$"(currently {_maxSessionSize}) or 'CreateSessionTimeout' " +
new YdbException($"The connection pool has been exhausted, either raise 'MaxPoolSize' " +
$"(currently {_maxSizePool}) or 'CreateSessionTimeout' " +
$"(currently {_createSessionTimeout} seconds) in your connection string.")
), useSynchronizationContext: false
);
Expand All @@ -151,7 +151,7 @@ private async ValueTask<ISession> RentAsync(CancellationToken cancellationToken)

private async ValueTask<T?> OpenNewSession(CancellationToken cancellationToken)
{
for (var numSessions = _numSessions; numSessions < _maxSessionSize; numSessions = _numSessions)
for (var numSessions = _numSessions; numSessions < _maxSizePool; numSessions = _numSessions)
{
if (Interlocked.CompareExchange(ref _numSessions, numSessions + 1, numSessions) != numSessions)
continue;
Expand All @@ -164,7 +164,7 @@ private async ValueTask<ISession> RentAsync(CancellationToken cancellationToken)
var session = _sessionFactory.NewSession(this);
await session.Open(cancellationToken);

for (var i = 0; i < _maxSessionSize; i++)
for (var i = 0; i < _maxSizePool; i++)
{
if (Interlocked.CompareExchange(ref _sessions[i], session, null) == null)
return session;
Expand Down Expand Up @@ -221,11 +221,11 @@ public void Return(T session)
private void CloseSession(T session)
{
var i = 0;
for (; i < _maxSessionSize; i++)
for (; i < _maxSizePool; i++)
if (Interlocked.CompareExchange(ref _sessions[i], null, session) == session)
break;

if (i == _maxSessionSize)
if (i == _maxSizePool)
return;

_ = session.DeleteSession();
Expand All @@ -242,13 +242,13 @@ private static void CleanIdleSessions(object? state)
var pool = (PoolingSessionSource<T>)state!;
var now = DateTime.Now;

for (var i = 0; i < pool._maxSessionSize; i++)
for (var i = 0; i < pool._maxSizePool; i++)
{
var session = Volatile.Read(ref pool._sessions[i]);

if (
session != null &&
pool._numSessions > pool._minSessionSize &&
pool._numSessions > pool._minSizePool &&
session.IdleStartTime + pool._sessionIdleTimeout <= now &&
session.CompareAndSet(PoolingSessionState.In, PoolingSessionState.Clean)
)
Expand All @@ -272,7 +272,7 @@ public async ValueTask DisposeAsync()
var spinWait = new SpinWait();
do
{
for (var i = 0; i < _maxSessionSize; i++)
for (var i = 0; i < _maxSizePool; i++)
{
var session = Volatile.Read(ref _sessions[i]);

Expand All @@ -294,7 +294,7 @@ public async ValueTask DisposeAsync()
_logger.LogError(e, "Failed to dispose the transport driver");
}

for (var i = 0; i < _maxSessionSize; i++)
for (var i = 0; i < _maxSizePool; i++)
{
var session = Volatile.Read(ref _sessions[i]);

Expand Down
40 changes: 20 additions & 20 deletions src/Ydb.Sdk/src/Ado/YdbConnectionStringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ private void InitDefaultValues()
_host = "localhost";
_port = 2136;
_database = "/local";
_minSessionPool = 0;
_maxSessionPool = 100;
_minPoolSize = 0;
_maxPoolSize = 100;
_createSessionTimeout = 5;
_sessionIdleTimeout = 300;
_useTls = false;
Expand Down Expand Up @@ -177,22 +177,22 @@ public string? Password
/// in the session pool. Must be greater than 0.
/// <para>Default value: 100.</para>
/// </remarks>
public int MaxSessionPool
public int MaxPoolSize
{
get => _maxSessionPool;
get => _maxPoolSize;
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid max session pool: " + value);
}

_maxSessionPool = value;
SaveValue(nameof(MaxSessionPool), value);
_maxPoolSize = value;
SaveValue(nameof(MaxPoolSize), value);
}
}

private int _maxSessionPool;
private int _maxPoolSize;

/// <summary>
/// Gets or sets the minimum number of sessions in the pool.
Expand All @@ -202,22 +202,22 @@ public int MaxSessionPool
/// Must be greater than or equal to 0.
/// <para>Default value: 0.</para>
/// </remarks>
public int MinSessionPool
public int MinPoolSize
{
get => _minSessionPool;
get => _minPoolSize;
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid min session pool: " + value);
}

_minSessionPool = value;
SaveValue(nameof(MinSessionPool), value);
_minPoolSize = value;
SaveValue(nameof(MinPoolSize), value);
}
}

private int _minSessionPool;
private int _minPoolSize;

/// <summary>
/// Gets or sets the session idle timeout in seconds.
Expand Down Expand Up @@ -392,7 +392,7 @@ public bool EnableMultipleHttp2Connections
/// Specifies the maximum size of messages that can be sent to the server.
/// Note: server-side limit is 64 MB. Exceeding this limit may result in
/// "resource exhausted" errors or unpredictable behavior.
/// <para>Default value: 4194304 bytes (4 MB).</para>
/// <para>Default value: 67108864 bytes (64 MB).</para>
/// </remarks>
public int MaxSendMessageSize
{
Expand All @@ -411,7 +411,7 @@ public int MaxSendMessageSize
/// </summary>
/// <remarks>
/// Specifies the maximum size of messages that can be received from the server.
/// <para>Default value: 4194304 bytes (4 MB).</para>
/// <para>Default value: 67108864 bytes (64 MB).</para>
/// </remarks>
public int MaxReceiveMessageSize
{
Expand Down Expand Up @@ -679,12 +679,12 @@ static YdbConnectionOption()
(builder, user) => builder.User = user), "User", "Username", "UserId", "User Id");
AddOption(new YdbConnectionOption<string>(StringExtractor,
(builder, password) => builder.Password = password), "Password", "PWD", "PSW");
AddOption(new YdbConnectionOption<int>(IntExtractor, (builder, maxSessionPool) =>
builder.MaxSessionPool = maxSessionPool), "MaxSessionPool", "Max Session Pool", "Maximum Pool Size",
"MaximumPoolSize", "Max Pool Size", "MaxPoolSize", "MaxSessionSize", "Max Session Size");
AddOption(new YdbConnectionOption<int>(IntExtractor, (builder, minSessionSize) =>
builder.MinSessionPool = minSessionSize), "MinSessionPool", "Min Session Pool", "Minimum Pool Size",
"MinimumPoolSize", "Min Pool Size", "MinPoolSize", "MinSessionSize", "Min Session Size");
AddOption(new YdbConnectionOption<int>(IntExtractor,
(builder, maxPoolSize) => builder.MaxPoolSize = maxPoolSize),
"Maximum Pool Size", "MaximumPoolSize", "Max Pool Size", "MaxPoolSize");
AddOption(new YdbConnectionOption<int>(IntExtractor,
(builder, minPoolSize) => builder.MinPoolSize = minPoolSize),
"Minimum Pool Size", "MinimumPoolSize", "Min Pool Size", "MinPoolSize");
AddOption(new YdbConnectionOption<bool>(BoolExtractor, (builder, useTls) => builder.UseTls = useTls),
"UseTls", "Use Tls");
AddOption(new YdbConnectionOption<string>(StringExtractor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class SessionSourceBenchmark
[GlobalSetup]
public void Setup()
{
var settings = new YdbConnectionStringBuilder { MaxSessionPool = SessionPoolSize };
var settings = new YdbConnectionStringBuilder { MaxPoolSize = SessionPoolSize };

_poolingSessionSource = new PoolingSessionSource<MockPoolingSession>(new MockSessionFactory(), settings);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ public class YdbFactoryFixture : IDbFactoryFixture
{
public DbProviderFactory Factory => YdbProviderFactory.Instance;

public string ConnectionString => "Host=localhost;Port=2136;Database=local;MaxSessionPool=10";
public string ConnectionString => "Host=localhost;Port=2136;Database=local;MaxPoolSize=10";
}
4 changes: 2 additions & 2 deletions src/Ydb.Sdk/test/Ydb.Sdk.Ado.Stress.Loader/LoadTank.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Starting YDB ADO.NET Stress Test Tank
var workers = new List<Task>();
ctsStep1.CancelAfter(_config.TotalTestTimeSeconds * 500);

for (var i = 0; i < _settings.MaxSessionPool; i++)
for (var i = 0; i < _settings.MaxPoolSize; i++)
{
workers.Add(Task.Run(async () =>
{
Expand Down Expand Up @@ -72,7 +72,7 @@ Starting YDB ADO.NET Stress Test Tank
_logger.LogInformation("[{Now}] Starting shooting without PoolingSessionSource...", DateTime.Now);
var ctsStep2 = new CancellationTokenSource();
ctsStep2.CancelAfter(_config.TotalTestTimeSeconds * 500);
for (var i = 0; i < _settings.MaxSessionPool; i++)
for (var i = 0; i < _settings.MaxPoolSize; i++)
{
workers.Add(Task.Run(async () =>
{
Expand Down
30 changes: 15 additions & 15 deletions src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/PoolManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,31 @@ public class PoolManagerTests
[Theory]
[InlineData(new[]
{
"MinSessionSize=1", "MinSessionSize=2", "MinSessionSize=3",
"MinSessionSize=1;DisableDiscovery=True", "MinSessionSize=2;DisableDiscovery=True"
"MinPoolSize=1", "MinPoolSize=2", "MinPoolSize=3",
"MinPoolSize=1;DisableDiscovery=True", "MinPoolSize=2;DisableDiscovery=True"
}, 2, 5)] // 2 transports (by the DisableDiscovery flag), 5 pools
[InlineData(
new[] { "MinSessionSize=1", "MinSessionSize=2", "MinSessionSize=3", "MinSessionSize=4", "MinSessionSize=5" },
new[] { "MinPoolSize=1", "MinPoolSize=2", "MinPoolSize=3", "MinPoolSize=4", "MinPoolSize=5" },
1, 5)] // 1 transport, 5 five pools
[InlineData(new[]
{ "MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=2", "MinSessionSize=2", "MinSessionSize=3" }, 1,
{ "MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=2", "MinPoolSize=2", "MinPoolSize=3" }, 1,
3)] // duplicate rows — we expect 1 transport, 3 pools
[InlineData(new[]
{
"MinSessionSize=1;ConnectTimeout=5", "MinSessionSize=1;ConnectTimeout=6", "MinSessionSize=1;ConnectTimeout=7",
"MinSessionSize=1;ConnectTimeout=8", "MinSessionSize=1;ConnectTimeout=9"
"MinPoolSize=1;ConnectTimeout=5", "MinPoolSize=1;ConnectTimeout=6", "MinPoolSize=1;ConnectTimeout=7",
"MinPoolSize=1;ConnectTimeout=8", "MinPoolSize=1;ConnectTimeout=9"
}, 5, 5)] // 5 transport, 5 five pools
[InlineData(new[] { "MinSessionSize=1" }, 1, 1)] // simple case
[InlineData(new[] { "MinPoolSize=1" }, 1, 1)] // simple case
[InlineData(new[]
{
"MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1",
"MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1",
"MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1",
"MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1", "MinSessionSize=1",
"MinSessionSize=2", "MinSessionSize=2", "MinSessionSize=2", "MinSessionSize=2", "MinSessionSize=2",
"MinSessionSize=2", "MinSessionSize=2", "MinSessionSize=2", "MinSessionSize=2", "MinSessionSize=2",
"MinSessionSize=2", "MinSessionSize=2", "MinSessionSize=2", "MinSessionSize=2", "MinSessionSize=3",
"MinSessionSize=3", "MinSessionSize=3", "MinSessionSize=3", "MinSessionSize=3", "MinSessionSize=3"
"MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1",
"MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1",
"MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1",
"MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1", "MinPoolSize=1",
"MinPoolSize=2", "MinPoolSize=2", "MinPoolSize=2", "MinPoolSize=2", "MinPoolSize=2",
"MinPoolSize=2", "MinPoolSize=2", "MinPoolSize=2", "MinPoolSize=2", "MinPoolSize=2",
"MinPoolSize=2", "MinPoolSize=2", "MinPoolSize=2", "MinPoolSize=2", "MinPoolSize=3",
"MinPoolSize=3", "MinPoolSize=3", "MinPoolSize=3", "MinPoolSize=3", "MinPoolSize=3"
}, 1, 3)] // duplicate rows — we expect 1 transport, 3 pools, stress test
public async Task PoolManager_CachingAndCleanup(string[] connectionStrings, int expectedDrivers, int expectedPools)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Ydb.Sdk.Ado.Tests.Session;

internal class MockPoolingSessionFactory(int maxSessionSize) : IPoolingSessionFactory<MockPoolingSession>
internal class MockPoolingSessionFactory(int maxPoolSize) : IPoolingSessionFactory<MockPoolingSession>
{
private int _sessionOpened;
private int _numSession;
Expand All @@ -25,7 +25,7 @@ public MockPoolingSession NewSession(PoolingSessionSource<MockPoolingSession> so
{
await Open(sessionCountOpened);

Assert.True(Interlocked.Increment(ref _numSession) <= maxSessionSize);
Assert.True(Interlocked.Increment(ref _numSession) <= maxPoolSize);

await Task.Yield();
},
Expand Down
Loading
Loading