Skip to content

Allow HttpClient instances to be refreshed periodically #4673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 27, 2020
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
1,253 changes: 636 additions & 617 deletions src/Elasticsearch.Net/Configuration/ConnectionConfiguration.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -249,5 +249,15 @@ public interface IConnectionConfigurationValues : IDisposable
/// Whether the request should be sent with chunked Transfer-Encoding.
/// </summary>
bool TransferEncodingChunked { get; }

/// <summary>
/// DnsRefreshTimeout for the connections. Defaults to 5 minutes.
#if DOTNETCORE
/// <para>Will create new instances of <see cref="System.Net.Http.HttpClient"/> after this timeout to force DNS updates</para>
#else
/// <para>Will set <see cref="System.Net.ServicePointManager.ConnectionLeaseTimeout "/>
#endif
/// </summary>
TimeSpan DnsRefreshTimeout { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if DOTNETCORE
using System;
using System.Diagnostics;
using System.Threading;

namespace Elasticsearch.Net
{
/// <summary>
/// Thread-safety: We treat this class as immutable except for the timer. Creating a new object
/// for the 'expiry' pool simplifies the threading requirements significantly.
/// <para>https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Http/src/ActiveHandlerTrackingEntry.cs</para>
/// </summary>
internal class ActiveHandlerTrackingEntry
{
private static readonly TimerCallback TimerCallback = (s) => ((ActiveHandlerTrackingEntry)s).Timer_Tick();
private readonly object _lock;
private bool _timerInitialized;
private Timer _timer;
private TimerCallback _callback;

public ActiveHandlerTrackingEntry(
int key,
LifetimeTrackingHttpMessageHandler handler,
TimeSpan lifetime)
{
Key = key;
Handler = handler;
Lifetime = lifetime;

_lock = new object();
}

public LifetimeTrackingHttpMessageHandler Handler { get; private set; }

public TimeSpan Lifetime { get; }

public int Key { get; }

public void StartExpiryTimer(TimerCallback callback)
{
if (Lifetime == Timeout.InfiniteTimeSpan) return;

if (Volatile.Read(ref _timerInitialized)) return;

StartExpiryTimerSlow(callback);
}

private void StartExpiryTimerSlow(TimerCallback callback)
{
Debug.Assert(Lifetime != Timeout.InfiniteTimeSpan);

lock (_lock)
{
if (Volatile.Read(ref _timerInitialized))
return;

_callback = callback;
_timer = NonCapturingTimer.Create(TimerCallback, this, Lifetime, Timeout.InfiniteTimeSpan);
_timerInitialized = true;
}
}

private void Timer_Tick()
{
Debug.Assert(_callback != null);
Debug.Assert(_timer != null);

lock (_lock)
{
if (_timer == null) return;

_timer.Dispose();
_timer = null;

_callback(this);
}
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if DOTNETCORE
using System;
using System.Net.Http;

namespace Elasticsearch.Net
{
/// <summary>
/// Thread-safety: This class is immutable
/// <para>https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs</para>
/// </summary>
internal class ExpiredHandlerTrackingEntry
{
private readonly WeakReference _livenessTracker;

// IMPORTANT: don't cache a reference to `other` or `other.Handler` here.
// We need to allow it to be GC'ed.
public ExpiredHandlerTrackingEntry(ActiveHandlerTrackingEntry other)
{
Key = other.Key;

_livenessTracker = new WeakReference(other.Handler);
InnerHandler = other.Handler.InnerHandler;
}

public bool CanDispose => !_livenessTracker.IsAlive;

public HttpMessageHandler InnerHandler { get; }

public int Key { get; }
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if DOTNETCORE
using System.Net.Http;

namespace Elasticsearch.Net
{
/// <summary>
/// This a marker used to check if the underlying handler should be disposed. HttpClients
/// share a reference to an instance of this class, and when it goes out of scope the inner handler
/// is eligible to be disposed.
/// <para>https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Http/src/LifetimeTrackingHttpMessageHandler.cs</para>
/// </summary>
internal class LifetimeTrackingHttpMessageHandler : DelegatingHandler
{
public LifetimeTrackingHttpMessageHandler(HttpMessageHandler innerHandler)
: base(innerHandler) { }

protected override void Dispose(bool disposing)
{
// The lifetime of this is tracked separately by ActiveHandlerTrackingEntry
}
}
}
#endif
Loading