diff --git a/src/Ydb.Sdk/CHANGELOG.md b/src/Ydb.Sdk/CHANGELOG.md index a4d7e72a..93dde306 100644 --- a/src/Ydb.Sdk/CHANGELOG.md +++ b/src/Ydb.Sdk/CHANGELOG.md @@ -1,3 +1,7 @@ +- 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`. + - `SessionPruningInterval`: How many seconds the pool waits before attempting to prune idle sessions (see `SessionIdleTimeout`). - Fixed bug `Reader`: unhandled exception in `TryReadRequestBytes(long bytes)`. - Handle `YdbException` on `DeleteSession`. - Do not invoke `DeleteSession` if the session is not active. diff --git a/src/Ydb.Sdk/src/Ado/YdbConnectionStringBuilder.cs b/src/Ydb.Sdk/src/Ado/YdbConnectionStringBuilder.cs index 669c2bf1..68c5849f 100644 --- a/src/Ydb.Sdk/src/Ado/YdbConnectionStringBuilder.cs +++ b/src/Ydb.Sdk/src/Ado/YdbConnectionStringBuilder.cs @@ -28,7 +28,11 @@ private void InitDefaultValues() _host = YdbAdoDefaultSettings.Host; _port = YdbAdoDefaultSettings.Port; _database = YdbAdoDefaultSettings.Database; + _minSessionPool = 0; _maxSessionPool = SessionPoolDefaultSettings.MaxSessionPool; + _createSessionTimeout = SessionPoolDefaultSettings.CreateSessionTimeoutSeconds; + _sessionIdleTimeout = 300; + _sessionPruningInterval = 10; _useTls = YdbAdoDefaultSettings.UseTls; _connectTimeout = GrpcDefaultSettings.ConnectTimeoutSeconds; _keepAlivePingDelay = GrpcDefaultSettings.KeepAlivePingSeconds; @@ -37,7 +41,6 @@ private void InitDefaultValues() _maxSendMessageSize = GrpcDefaultSettings.MaxSendMessageSize; _maxReceiveMessageSize = GrpcDefaultSettings.MaxReceiveMessageSize; _disableDiscovery = GrpcDefaultSettings.DisableDiscovery; - _createSessionTimeout = SessionPoolDefaultSettings.CreateSessionTimeoutSeconds; _disableServerBalancer = false; } @@ -123,6 +126,58 @@ public int MaxSessionPool private int _maxSessionPool; + public int MinSessionPool + { + get => _minSessionPool; + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid min session pool: " + value); + } + + _minSessionPool = value; + SaveValue(nameof(MinSessionPool), value); + } + } + + private int _minSessionPool; + + public int SessionIdleTimeout + { + get => _sessionIdleTimeout; + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid session idle timeout: " + value); + } + + _sessionIdleTimeout = value; + SaveValue(nameof(SessionIdleTimeout), value); + } + } + + private int _sessionIdleTimeout; + + public int SessionPruningInterval + { + get => _sessionPruningInterval; + set + { + if (value <= 0) + { + throw new ArgumentOutOfRangeException(nameof(value), value, + "Invalid session pruning interval: " + value); + } + + _sessionPruningInterval = value; + SaveValue(nameof(SessionPruningInterval), value); + } + } + + private int _sessionPruningInterval; + public bool UseTls { get => _useTls; @@ -416,9 +471,12 @@ static YdbConnectionOption() (builder, user) => builder.User = user), "User", "Username", "UserId", "User Id"); AddOption(new YdbConnectionOption(StringExtractor, (builder, password) => builder.Password = password), "Password", "PWD", "PSW"); - AddOption(new YdbConnectionOption(IntExtractor, - (builder, maxSessionPool) => builder.MaxSessionPool = maxSessionPool), - "MaxSessionPool", "Max Session Pool", "Maximum Pool Size", "Max Pool Size", "MaximumPoolSize"); + AddOption(new YdbConnectionOption(IntExtractor, (builder, maxSessionPool) => + builder.MaxSessionPool = maxSessionPool), "MaxSessionPool", "Max Session Pool", "Maximum Pool Size", + "MaximumPoolSize", "Max Pool Size", "MaxPoolSize"); + AddOption(new YdbConnectionOption(IntExtractor, (builder, minSessionSize) => + builder.MinSessionPool = minSessionSize), "MinSessionPool", "Min Session Pool", "Minimum Pool Size", + "MinimumPoolSize", "Min Pool Size", "MinPoolSize"); AddOption(new YdbConnectionOption(BoolExtractor, (builder, useTls) => builder.UseTls = useTls), "UseTls", "Use Tls"); AddOption(new YdbConnectionOption(StringExtractor, @@ -446,8 +504,14 @@ static YdbConnectionOption() AddOption(new YdbConnectionOption(IntExtractor, (builder, createSessionTimeout) => builder.CreateSessionTimeout = createSessionTimeout), "CreateSessionTimeout", "Create Session Timeout"); - AddOption(new YdbConnectionOption(BoolExtractor, (builder, disableServerBalancer) => - builder.DisableServerBalancer = disableServerBalancer), + AddOption(new YdbConnectionOption(IntExtractor, + (builder, sessionIdleTimeout) => builder.SessionIdleTimeout = sessionIdleTimeout), + "SessionIdleTimeout", "Session Idle Timeout"); + AddOption(new YdbConnectionOption(IntExtractor, + (builder, sessionPruningInterval) => builder.SessionPruningInterval = sessionPruningInterval), + "SessionPruningInterval", "Session Pruning Interval"); + AddOption(new YdbConnectionOption(BoolExtractor, + (builder, disableServerBalancer) => builder.DisableServerBalancer = disableServerBalancer), "DisableServerBalancer", "Disable Server Balancer"); } diff --git a/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbConnectionStringBuilderTests.cs b/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbConnectionStringBuilderTests.cs index c892c896..62372c4d 100644 --- a/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbConnectionStringBuilderTests.cs +++ b/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbConnectionStringBuilderTests.cs @@ -12,7 +12,11 @@ public void InitDefaultValues_WhenEmptyConstructorInvoke_ReturnDefaultConnection Assert.Equal(2136, ydbConnectionStringBuilder.Port); Assert.Equal("localhost", ydbConnectionStringBuilder.Host); Assert.Equal("/local", ydbConnectionStringBuilder.Database); + Assert.Equal(0, ydbConnectionStringBuilder.MinSessionPool); Assert.Equal(100, ydbConnectionStringBuilder.MaxSessionPool); + Assert.Equal(5, ydbConnectionStringBuilder.CreateSessionTimeout); + Assert.Equal(300, ydbConnectionStringBuilder.SessionIdleTimeout); + Assert.Equal(10, ydbConnectionStringBuilder.SessionPruningInterval); Assert.Null(ydbConnectionStringBuilder.User); Assert.Null(ydbConnectionStringBuilder.Password); Assert.Equal(5, ydbConnectionStringBuilder.ConnectTimeout); @@ -24,7 +28,6 @@ public void InitDefaultValues_WhenEmptyConstructorInvoke_ReturnDefaultConnection Assert.Equal(64 * 1024 * 1024, ydbConnectionStringBuilder.MaxReceiveMessageSize); Assert.False(ydbConnectionStringBuilder.DisableDiscovery); Assert.False(ydbConnectionStringBuilder.DisableServerBalancer); - Assert.Equal(5, ydbConnectionStringBuilder.CreateSessionTimeout); Assert.False(ydbConnectionStringBuilder.UseTls); } @@ -43,17 +46,22 @@ public void InitConnectionStringBuilder_WhenExpectedKeys_ReturnUpdatedConnection { var connectionString = new YdbConnectionStringBuilder( "Host=server;Port=2135;Database=/my/path;User=Kirill;UseTls=true;" + + "MinSessionPool=10;MaxSessionPool=50;CreateSessionTimeout=30;" + + "SessionIdleTimeout=600;SessionPruningInterval=20;" + "ConnectTimeout=30;KeepAlivePingDelay=30;KeepAlivePingTimeout=60;" + - "EnableMultipleHttp2Connections=true;CreateSessionTimeout=30;" + + "EnableMultipleHttp2Connections=true;" + "MaxSendMessageSize=1000000;MaxReceiveMessageSize=1000000;" + - "DisableDiscovery=true;DisableServerBalancer=true;" + - "MaxSessionPool=50" + "DisableDiscovery=true;DisableServerBalancer=true;" ); Assert.Equal(2135, connectionString.Port); Assert.Equal("server", connectionString.Host); Assert.Equal("/my/path", connectionString.Database); + Assert.Equal(10, connectionString.MinSessionPool); Assert.Equal(50, connectionString.MaxSessionPool); + Assert.Equal(30, connectionString.CreateSessionTimeout); + Assert.Equal(600, connectionString.SessionIdleTimeout); + Assert.Equal(20, connectionString.SessionPruningInterval); Assert.Equal("Kirill", connectionString.User); Assert.Equal(30, connectionString.ConnectTimeout); Assert.Equal(30, connectionString.KeepAlivePingDelay); @@ -63,15 +71,14 @@ public void InitConnectionStringBuilder_WhenExpectedKeys_ReturnUpdatedConnection Assert.Equal(1000000, connectionString.MaxSendMessageSize); Assert.Equal(1000000, connectionString.MaxReceiveMessageSize); Assert.Equal("Host=server;Port=2135;Database=/my/path;User=Kirill;UseTls=True;" + + "MinSessionPool=10;MaxSessionPool=50;CreateSessionTimeout=30;" + + "SessionIdleTimeout=600;SessionPruningInterval=20;" + "ConnectTimeout=30;KeepAlivePingDelay=30;KeepAlivePingTimeout=60;" + - "EnableMultipleHttp2Connections=True;CreateSessionTimeout=30;" + + "EnableMultipleHttp2Connections=True;" + "MaxSendMessageSize=1000000;MaxReceiveMessageSize=1000000;" + - "DisableDiscovery=True;DisableServerBalancer=True;" + - "MaxSessionPool=50", connectionString.ConnectionString); + "DisableDiscovery=True;DisableServerBalancer=True", connectionString.ConnectionString); Assert.True(connectionString.DisableDiscovery); Assert.True(connectionString.DisableServerBalancer); - Assert.Equal(30, connectionString.CreateSessionTimeout); - Assert.Equal(50, connectionString.MaxSessionPool); } [Fact] diff --git a/src/Ydb.Sdk/test/Ydb.Sdk.Topic.Tests/ReaderUnitTests.cs b/src/Ydb.Sdk/test/Ydb.Sdk.Topic.Tests/ReaderUnitTests.cs index 4052e84c..8d5795f9 100644 --- a/src/Ydb.Sdk/test/Ydb.Sdk.Topic.Tests/ReaderUnitTests.cs +++ b/src/Ydb.Sdk/test/Ydb.Sdk.Topic.Tests/ReaderUnitTests.cs @@ -1093,7 +1093,7 @@ public async Task _mockStream.Verify(stream => stream.Write(It.Is(msg => msg.InitRequest != null && msg.InitRequest.Consumer == "Consumer" && - msg.InitRequest.TopicsReadSettings[0].Path == "/topic")), Times.Between(2, 3, Range.Inclusive)); + msg.InitRequest.TopicsReadSettings[0].Path == "/topic")), Times.AtLeast(2)); _mockStream.Verify(stream => stream.Write(It.Is(msg => msg.ReadRequest != null && msg.ReadRequest.BytesSize == 100)), Times.Exactly(2)); _mockStream.Verify(stream => stream.Write(It.Is(msg =>