Skip to content

Commit 903bba2

Browse files
authored
[Backport 6.x] Reimplement #4673 manually (#4678)
1 parent 0543aed commit 903bba2

File tree

10 files changed

+497
-49
lines changed

10 files changed

+497
-49
lines changed

dotnet-tools.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,11 @@
88
"assembly-rewriter"
99
]
1010
},
11-
"dotnet-assembly-differ": {
12-
"version": "1.0.0-ci20190926135713",
13-
"commands": [
14-
"assembly-differ"
15-
]
16-
},
1711
"assembly-differ": {
1812
"version": "0.9.0",
1913
"commands": [
2014
"assembly-differ"
2115
]
2216
}
2317
}
24-
}
18+
}

src/Elasticsearch.Net/Configuration/ConnectionConfiguration.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ private static bool UsingCurlHandler
7676
/// </summary>
7777
public static readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(1);
7878

79+
/// <summary>
80+
/// The default timeout before a TCP connection is forcefully recycled so that DNS updates come through
81+
/// Defaults to 5 minutes.
82+
/// </summary>
83+
public static readonly TimeSpan DefaultDnsRefreshTimeout = TimeSpan.FromMinutes(5);
84+
7985
/// <summary>
8086
/// The default connection limit for both Elasticsearch.Net and Nest. Defaults to <c>80</c>
8187
#if DOTNETCORE
@@ -185,6 +191,7 @@ public abstract class ConnectionConfiguration<T> : IConnectionConfigurationValue
185191
private string _proxyPassword;
186192
private string _proxyUsername;
187193
private TimeSpan _requestTimeout;
194+
private TimeSpan _dnsRefreshTimeout;
188195
private Func<object, X509Certificate, X509Chain, SslPolicyErrors, bool> _serverCertificateValidationCallback;
189196
private IReadOnlyCollection<int> _skipDeserializationForStatusCodes = new ReadOnlyCollection<int>(new int[] { });
190197
private TimeSpan? _sniffLifeSpan;
@@ -202,6 +209,7 @@ protected ConnectionConfiguration(IConnectionPool connectionPool, IConnection co
202209

203210
_connectionLimit = ConnectionConfiguration.DefaultConnectionLimit;
204211
_requestTimeout = ConnectionConfiguration.DefaultTimeout;
212+
_dnsRefreshTimeout = ConnectionConfiguration.DefaultDnsRefreshTimeout;
205213
_sniffOnConnectionFault = true;
206214
_sniffOnStartup = true;
207215
_sniffLifeSpan = TimeSpan.FromHours(1);
@@ -249,6 +257,7 @@ protected ConnectionConfiguration(IConnectionPool connectionPool, IConnection co
249257
NameValueCollection IConnectionConfigurationValues.QueryStringParameters => _queryString;
250258
IElasticsearchSerializer IConnectionConfigurationValues.RequestResponseSerializer => UseThisRequestResponseSerializer;
251259
TimeSpan IConnectionConfigurationValues.RequestTimeout => _requestTimeout;
260+
TimeSpan IConnectionConfigurationValues.DnsRefreshTimeout => _dnsRefreshTimeout;
252261

253262
Func<object, X509Certificate, X509Chain, SslPolicyErrors, bool> IConnectionConfigurationValues.ServerCertificateValidationCallback =>
254263
_serverCertificateValidationCallback;
@@ -419,6 +428,16 @@ public T GlobalQueryStringParameters(NameValueCollection queryStringParameters)
419428
/// </summary>
420429
public T MaxRetryTimeout(TimeSpan maxRetryTimeout) => Assign(maxRetryTimeout, (a, v) => a._maxRetryTimeout = v);
421430

431+
/// <summary>
432+
/// DnsRefreshTimeout for the connections. Defaults to 5 minutes.
433+
#if DOTNETCORE
434+
/// <para>Will create new instances of <see cref="System.Net.Http.HttpClient"/> after this timeout to force DNS updates</para>
435+
#else
436+
/// <para>Will set both <see cref="System.Net.ServicePointManager.DnsRefreshTimeout"/> and <see cref="System.Net.ServicePointManager.ConnectionLeaseTimeout "/>
437+
#endif
438+
/// </summary>
439+
public T DnsRefreshTimeout(TimeSpan timeout) => Assign(timeout, (a, v) => a._dnsRefreshTimeout = v);
440+
422441
/// <summary>
423442
/// If your connection has to go through proxy, use this method to specify the proxy url
424443
/// </summary>

src/Elasticsearch.Net/Configuration/IConnectionConfigurationValues.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,5 +219,15 @@ public interface IConnectionConfigurationValues : IDisposable
219219
/// versions that initiate requests to Elasticsearch
220220
/// </summary>
221221
string UserAgent { get; }
222+
223+
/// <summary>
224+
/// DnsRefreshTimeout for the connections. Defaults to 5 minutes.
225+
#if DOTNETCORE
226+
/// <para>Will create new instances of <see cref="System.Net.Http.HttpClient"/> after this timeout to force DNS updates</para>
227+
#else
228+
/// <para>Will set <see cref="System.Net.ServicePointManager.ConnectionLeaseTimeout "/>
229+
#endif
230+
/// </summary>
231+
TimeSpan DnsRefreshTimeout { get; }
222232
}
223233
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if DOTNETCORE
6+
using System;
7+
using System.Diagnostics;
8+
using System.Threading;
9+
10+
namespace Elasticsearch.Net
11+
{
12+
/// <summary>
13+
/// Thread-safety: We treat this class as immutable except for the timer. Creating a new object
14+
/// for the 'expiry' pool simplifies the threading requirements significantly.
15+
/// <para>https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Http/src/ActiveHandlerTrackingEntry.cs</para>
16+
/// </summary>
17+
internal class ActiveHandlerTrackingEntry
18+
{
19+
private static readonly TimerCallback TimerCallback = (s) => ((ActiveHandlerTrackingEntry)s).Timer_Tick();
20+
private readonly object _lock;
21+
private bool _timerInitialized;
22+
private Timer _timer;
23+
private TimerCallback _callback;
24+
25+
public ActiveHandlerTrackingEntry(
26+
int key,
27+
LifetimeTrackingHttpMessageHandler handler,
28+
TimeSpan lifetime)
29+
{
30+
Key = key;
31+
Handler = handler;
32+
Lifetime = lifetime;
33+
34+
_lock = new object();
35+
}
36+
37+
public LifetimeTrackingHttpMessageHandler Handler { get; private set; }
38+
39+
public TimeSpan Lifetime { get; }
40+
41+
public int Key { get; }
42+
43+
public void StartExpiryTimer(TimerCallback callback)
44+
{
45+
if (Lifetime == Timeout.InfiniteTimeSpan) return;
46+
47+
if (Volatile.Read(ref _timerInitialized)) return;
48+
49+
StartExpiryTimerSlow(callback);
50+
}
51+
52+
private void StartExpiryTimerSlow(TimerCallback callback)
53+
{
54+
Debug.Assert(Lifetime != Timeout.InfiniteTimeSpan);
55+
56+
lock (_lock)
57+
{
58+
if (Volatile.Read(ref _timerInitialized))
59+
return;
60+
61+
_callback = callback;
62+
_timer = NonCapturingTimer.Create(TimerCallback, this, Lifetime, Timeout.InfiniteTimeSpan);
63+
_timerInitialized = true;
64+
}
65+
}
66+
67+
private void Timer_Tick()
68+
{
69+
Debug.Assert(_callback != null);
70+
Debug.Assert(_timer != null);
71+
72+
lock (_lock)
73+
{
74+
if (_timer == null) return;
75+
76+
_timer.Dispose();
77+
_timer = null;
78+
79+
_callback(this);
80+
}
81+
}
82+
}
83+
}
84+
#endif
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if DOTNETCORE
6+
using System;
7+
using System.Net.Http;
8+
9+
namespace Elasticsearch.Net
10+
{
11+
/// <summary>
12+
/// Thread-safety: This class is immutable
13+
/// <para>https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs</para>
14+
/// </summary>
15+
internal class ExpiredHandlerTrackingEntry
16+
{
17+
private readonly WeakReference _livenessTracker;
18+
19+
// IMPORTANT: don't cache a reference to `other` or `other.Handler` here.
20+
// We need to allow it to be GC'ed.
21+
public ExpiredHandlerTrackingEntry(ActiveHandlerTrackingEntry other)
22+
{
23+
Key = other.Key;
24+
25+
_livenessTracker = new WeakReference(other.Handler);
26+
InnerHandler = other.Handler.InnerHandler;
27+
}
28+
29+
public bool CanDispose => !_livenessTracker.IsAlive;
30+
31+
public HttpMessageHandler InnerHandler { get; }
32+
33+
public int Key { get; }
34+
}
35+
}
36+
#endif
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if DOTNETCORE
6+
using System.Net.Http;
7+
8+
namespace Elasticsearch.Net
9+
{
10+
/// <summary>
11+
/// This a marker used to check if the underlying handler should be disposed. HttpClients
12+
/// share a reference to an instance of this class, and when it goes out of scope the inner handler
13+
/// is eligible to be disposed.
14+
/// <para>https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Http/src/LifetimeTrackingHttpMessageHandler.cs</para>
15+
/// </summary>
16+
internal class LifetimeTrackingHttpMessageHandler : DelegatingHandler
17+
{
18+
public LifetimeTrackingHttpMessageHandler(HttpMessageHandler innerHandler)
19+
: base(innerHandler) { }
20+
21+
protected override void Dispose(bool disposing)
22+
{
23+
// The lifetime of this is tracked separately by ActiveHandlerTrackingEntry
24+
}
25+
}
26+
}
27+
#endif

0 commit comments

Comments
 (0)