Skip to content

Commit 4228930

Browse files
feat: Added KeepAlivePingDelay and KeepAlivePingTimeout (#290)
1 parent 21517c6 commit 4228930

File tree

7 files changed

+108
-16
lines changed

7 files changed

+108
-16
lines changed

.github/scripts/publish.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ else
3737
VERSION="$MAJOR.$MINOR.$PATCH";
3838
fi;
3939
TAG="v$VERSION"
40-
echo "## v$VERSION" >> $CHANGELOG_FILE.tmp
40+
echo "## v$VERSION\n" >> $CHANGELOG_FILE.tmp
4141
cat $CHANGELOG_FILE >> $CHANGELOG_FILE.tmp
4242
mv $CHANGELOG_FILE.tmp $CHANGELOG_FILE;
4343
git add $CHANGELOG_FILE;

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1+
- Added `KeepAlivePingTimeout`, with a default value of 10 seconds.
2+
- Added `KeepAlivePingDelay`, with a default value of 10 seconds.
3+
14
## v0.15.3
5+
26
- Added SeqNo to `Ydb.Sdk.Services.Topic.Reader.Message`.
37

48
## v0.15.2
9+
510
- Added SeqNo to `WriteResult`.
611
- Changed signature of the `TopicClient.DropTopic` method.
712

813
## v0.15.1
9-
- Fixed Writer: possible creation of a session after `DisposeAsync()`, which this could happen when there are canceled tasks in `InFlightMessages`.
14+
15+
- Fixed Writer: possible creation of a session after `DisposeAsync()`, which this could happen when there are canceled
16+
tasks in `InFlightMessages`.
1017
- Dev: `Writer.MoveNext()` changed exception on cancelToken from `WriterException` to `TaskCanceledException`.
1118
- Dev: changed log level from `Warning` to `Information` in `(Reader / Writer).Initialize()` when it is disposed.
1219

1320
## v0.15.0
21+
1422
- Dev: added `ValueTask<string?> GetAuthInfoAsync()` in ICredentialProvider.
1523
- Feat: `Writer.DisposeAsync()` waits for all in-flight messages to complete.
1624
- Feat: `Reader.DisposeAsync()` waits for all pending commits to be completed.

src/Ydb.Sdk/src/Ado/YdbConnectionStringBuilder.cs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ private void InitDefaultValues()
2727
_database = "/local";
2828
_maxSessionPool = 100;
2929
_useTls = false;
30+
_keepAlivePingDelay = SocketHttpHandlerDefaults.DefaultKeepAlivePingSeconds;
31+
_keepAlivePingTimeout = SocketHttpHandlerDefaults.DefaultKeepAlivePingTimeoutSeconds;
3032
}
3133

3234
public string Host
@@ -137,6 +139,41 @@ public string? RootCertificate
137139

138140
private string? _rootCertificate;
139141

142+
public int KeepAlivePingDelay
143+
{
144+
get => _keepAlivePingDelay;
145+
set
146+
{
147+
if (value < 0)
148+
{
149+
throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid keep alive ping delay: " + value);
150+
}
151+
152+
_keepAlivePingDelay = value;
153+
SaveValue(nameof(KeepAlivePingDelay), value);
154+
}
155+
}
156+
157+
private int _keepAlivePingDelay;
158+
159+
public int KeepAlivePingTimeout
160+
{
161+
get => _keepAlivePingTimeout;
162+
set
163+
{
164+
if (value < 0)
165+
{
166+
throw new ArgumentOutOfRangeException(nameof(value), value,
167+
"Invalid keep alive ping timeout: " + value);
168+
}
169+
170+
_keepAlivePingTimeout = value;
171+
SaveValue(nameof(KeepAlivePingTimeout), value);
172+
}
173+
}
174+
175+
private int _keepAlivePingTimeout;
176+
140177
public ILoggerFactory? LoggerFactory { get; init; }
141178

142179
public ICredentialsProvider? CredentialsProvider { get; init; }
@@ -193,7 +230,15 @@ internal Task<Driver> BuildDriver()
193230
credentials: credentialsProvider,
194231
customServerCertificate: cert,
195232
customServerCertificates: ServerCertificates
196-
), LoggerFactory);
233+
)
234+
{
235+
KeepAlivePingDelay = KeepAlivePingDelay == 0
236+
? Timeout.InfiniteTimeSpan
237+
: TimeSpan.FromSeconds(KeepAlivePingDelay),
238+
KeepAlivePingTimeout = KeepAlivePingTimeout == 0
239+
? Timeout.InfiniteTimeSpan
240+
: TimeSpan.FromSeconds(KeepAlivePingTimeout)
241+
}, LoggerFactory);
197242
}
198243

199244
public override void Clear()
@@ -265,6 +310,12 @@ static YdbConnectionOption()
265310
new YdbConnectionOption<string>(StringExtractor,
266311
(builder, rootCertificate) => builder.RootCertificate = rootCertificate),
267312
"RootCertificate", "Root Certificate");
313+
AddOption(new YdbConnectionOption<int>(IntExtractor,
314+
(builder, keepAlivePingDelay) => builder.KeepAlivePingDelay = keepAlivePingDelay),
315+
"KeepAlivePingDelay", "Keep Alive Ping Delay");
316+
AddOption(new YdbConnectionOption<int>(IntExtractor,
317+
(builder, keepAlivePingTimeout) => builder.KeepAlivePingTimeout = keepAlivePingTimeout),
318+
"KeepAlivePingTimeout", "Keep Alive Ping Timeout");
268319
}
269320

270321
private static void AddOption(YdbConnectionOption option, params string[] keys)

src/Ydb.Sdk/src/DriverConfig.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ public class DriverConfig
99
public string Database { get; }
1010
public ICredentialsProvider Credentials { get; }
1111

12+
public TimeSpan KeepAlivePingDelay { get; set; } =
13+
TimeSpan.FromSeconds(SocketHttpHandlerDefaults.DefaultKeepAlivePingSeconds);
14+
15+
public TimeSpan KeepAlivePingTimeout { get; set; } =
16+
TimeSpan.FromSeconds(SocketHttpHandlerDefaults.DefaultKeepAlivePingTimeoutSeconds);
17+
1218
internal X509Certificate2Collection CustomServerCertificates { get; } = new();
1319
internal TimeSpan EndpointDiscoveryInterval = TimeSpan.FromMinutes(1);
1420
internal TimeSpan EndpointDiscoveryTimeout = TimeSpan.FromSeconds(10);

src/Ydb.Sdk/src/Pool/ChannelPool.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,15 @@ internal class GrpcChannelFactory : IChannelFactory<GrpcChannel>
7777
{
7878
private readonly ILoggerFactory _loggerFactory;
7979
private readonly ILogger<GrpcChannelFactory> _logger;
80-
private readonly X509Certificate2Collection _x509Certificate2Collection;
80+
private readonly DriverConfig _config;
81+
82+
private X509Certificate2Collection ServerCertificates => _config.CustomServerCertificates;
8183

8284
internal GrpcChannelFactory(ILoggerFactory loggerFactory, DriverConfig config)
8385
{
8486
_loggerFactory = loggerFactory;
8587
_logger = loggerFactory.CreateLogger<GrpcChannelFactory>();
86-
_x509Certificate2Collection = config.CustomServerCertificates;
88+
_config = config;
8789
}
8890

8991
public GrpcChannel CreateChannel(string endpoint)
@@ -92,18 +94,24 @@ public GrpcChannel CreateChannel(string endpoint)
9294

9395
var channelOptions = new GrpcChannelOptions
9496
{
95-
LoggerFactory = _loggerFactory
97+
LoggerFactory = _loggerFactory,
98+
DisposeHttpClient = true
9699
};
97100

98-
var httpHandler = new SocketsHttpHandler();
101+
var httpHandler = new SocketsHttpHandler
102+
{
103+
// https://github.com/grpc/proposal/blob/master/A8-client-side-keepalive.md
104+
KeepAlivePingDelay = _config.KeepAlivePingDelay,
105+
KeepAlivePingTimeout = _config.KeepAlivePingTimeout,
106+
KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always
107+
};
99108

100109
// https://github.com/grpc/grpc-dotnet/issues/2312#issuecomment-1790661801
101110
httpHandler.Properties["__GrpcLoadBalancingDisabled"] = true;
102111

103112
channelOptions.HttpHandler = httpHandler;
104-
channelOptions.DisposeHttpClient = true;
105113

106-
if (_x509Certificate2Collection.Count == 0)
114+
if (ServerCertificates.Count == 0)
107115
{
108116
return GrpcChannel.ForAddress(endpoint, channelOptions);
109117
}
@@ -124,11 +132,11 @@ public GrpcChannel CreateChannel(string endpoint)
124132
try
125133
{
126134
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
127-
chain.ChainPolicy.ExtraStore.AddRange(_x509Certificate2Collection);
135+
chain.ChainPolicy.ExtraStore.AddRange(ServerCertificates);
128136

129-
return chain.Build(new X509Certificate2(certificate)) && chain.ChainElements.Any(chainElement =>
130-
_x509Certificate2Collection.Any(trustedCert =>
131-
chainElement.Certificate.Thumbprint == trustedCert.Thumbprint));
137+
return chain.Build(new X509Certificate2(certificate)) &&
138+
chain.ChainElements.Any(chainElement => ServerCertificates.Any(trustedCert =>
139+
chainElement.Certificate.Thumbprint == trustedCert.Thumbprint));
132140
}
133141
catch (Exception e)
134142
{
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace Ydb.Sdk;
2+
3+
internal static class SocketHttpHandlerDefaults
4+
{
5+
/// <summary>
6+
/// Default interval (in seconds) for sending keep-alive ping messages.
7+
/// </summary>
8+
internal const int DefaultKeepAlivePingSeconds = 10;
9+
10+
/// <summary>
11+
/// Default timeout (in seconds) for receiving a response to a keep-alive ping.
12+
/// </summary>
13+
internal const int DefaultKeepAlivePingTimeoutSeconds = 10;
14+
}

src/Ydb.Sdk/tests/Ado/YdbConnectionStringBuilderTests.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public void InitDefaultValues_WhenEmptyConstructorInvoke_ReturnDefaultConnection
1616
Assert.Equal(100, connectionString.MaxSessionPool);
1717
Assert.Null(connectionString.User);
1818
Assert.Null(connectionString.Password);
19+
Assert.Equal(10, connectionString.KeepAlivePingDelay);
20+
Assert.Equal(10, connectionString.KeepAlivePingTimeout);
1921
Assert.Equal("", connectionString.ConnectionString);
2022
}
2123

@@ -33,16 +35,19 @@ public void InitConnectionStringBuilder_WhenUnexpectedKey_ThrowException()
3335
public void InitConnectionStringBuilder_WhenExpectedKeys_ReturnUpdatedConnectionString()
3436
{
3537
var connectionString =
36-
new YdbConnectionStringBuilder("Host=server;Port=2135;Database=/my/path;User=Kirill;UseTls=true");
38+
new YdbConnectionStringBuilder("Host=server;Port=2135;Database=/my/path;User=Kirill;UseTls=true;" +
39+
"KeepAlivePingDelay=30;KeepAlivePingTimeout=60");
3740

3841
Assert.Equal(2135, connectionString.Port);
3942
Assert.Equal("server", connectionString.Host);
4043
Assert.Equal("/my/path", connectionString.Database);
4144
Assert.Equal(100, connectionString.MaxSessionPool);
4245
Assert.Equal("Kirill", connectionString.User);
46+
Assert.Equal(30, connectionString.KeepAlivePingDelay);
47+
Assert.Equal(60, connectionString.KeepAlivePingTimeout);
4348
Assert.Null(connectionString.Password);
44-
Assert.Equal("Host=server;Port=2135;Database=/my/path;User=Kirill;UseTls=True",
45-
connectionString.ConnectionString);
49+
Assert.Equal("Host=server;Port=2135;Database=/my/path;User=Kirill;UseTls=True;" +
50+
"KeepAlivePingDelay=30;KeepAlivePingTimeout=60", connectionString.ConnectionString);
4651
}
4752

4853
[Fact]

0 commit comments

Comments
 (0)