Skip to content
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

API: ConfigureEndpoints for Proxies #2383

Closed
wants to merge 10 commits into from
Closed
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
95 changes: 94 additions & 1 deletion src/StackExchange.Redis/ConnectionMultiplexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public sealed partial class ConnectionMultiplexer : IInternalConnectionMultiplex
internal bool IsDisposed => _isDisposed;

internal CommandMap CommandMap { get; }
internal EndPointCollection EndPoints { get; }
internal EndPointCollection EndPoints { get; set; }
internal ConfigurationOptions RawConfig { get; }
internal ServerSelectionStrategy ServerSelectionStrategy { get; }
internal Exception? LastException { get; set; }
Expand Down Expand Up @@ -1200,6 +1200,99 @@ public async Task<bool> ConfigureAsync(TextWriter? log = null)
return await ReconfigureAsync(first: false, reconfigureAll: true, logProxy, null, "configure").ObserveErrors().ForAwait();
}


/// <summary>
/// Reconfigure the connections based on a new endpoint string. For refreshing proxy connections.
/// </summary>
/// <param name="endpoints">Comma delimited string of new endpoints to configure with</param>
/// <param name="log">The <see cref="TextWriter"/> to log to.</param>
public bool ConfigureEndPoints(string endpoints, TextWriter? log = null)
{
// Reset the slot balance for proxies
if (RawConfig.Proxy == Proxy.None)
{
return true;
}

var newEndpoints = new EndPointCollection();
endpoints.Split(',').ToList().ForEach(endpoint => {
if (!string.IsNullOrWhiteSpace(endpoint)) return;
var parsedEndpoint = EndPointCollection.TryParse(endpoint);
if (parsedEndpoint == null) return;
newEndpoints.TryAdd(parsedEndpoint);
});

if (newEndpoints.Equals(EndPoints))
{
return true;
}

// Mark obsolete connections as unselectable
var obsoleteEndpoints = EndPoints.ToList().Except(newEndpoints).ToList();
foreach (var oldEndpoint in obsoleteEndpoints)
{
((ServerEndPoint?)servers[oldEndpoint])?.SetUnselectable(UnselectableFlags.DidNotRespond);
}

// Reconfigure with new connections
EndPoints = new EndPointCollection(newEndpoints);

if (!Configure(log))
{
return false;
}

ServerSelectionStrategy.ResetMap();

return true;
}

/// <summary>
/// Reconfigure the connections based on a new endpoint string. For refreshing proxy connections.
/// </summary>
/// <param name="endpoints">Comma delimited string of new endpoints to configure with</param>
/// <param name="log">The <see cref="TextWriter"/> to log to.</param>
public async Task<bool> ConfigureEndPointsAsync(string endpoints, TextWriter? log = null)
{
if (RawConfig.Proxy == Proxy.None)
{
return true;
}

var newEndpoints = new EndPointCollection();
endpoints.Split(',').ToList().ForEach(endpoint => {
if (!string.IsNullOrWhiteSpace(endpoint)) return;
var parsedEndpoint = EndPointCollection.TryParse(endpoint);
if (parsedEndpoint == null) return;
newEndpoints.TryAdd(parsedEndpoint);
});

if (newEndpoints.Equals(EndPoints))
{
return true;
}

// Mark obsolete connections as unselectable
var obsoleteEndpoints = EndPoints.ToList().Except(newEndpoints).ToList();
foreach (var oldEndpoint in obsoleteEndpoints)
{
((ServerEndPoint?)servers[oldEndpoint])?.SetUnselectable(UnselectableFlags.DidNotRespond);
}

// Reconfigure with new connections
EndPoints = new EndPointCollection(newEndpoints);

if (!await ConfigureAsync(log))
{
return false;
}

// Reset the slot balance for proxies
ServerSelectionStrategy.ResetMap();

return true;
}

internal int SyncConnectTimeout(bool forConnect)
{
int retryCount = forConnect ? RawConfig.ConnectRetry : 1;
Expand Down
14 changes: 14 additions & 0 deletions src/StackExchange.Redis/Interfaces/IConnectionMultiplexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,20 @@ public interface IConnectionMultiplexer : IDisposable, IAsyncDisposable
/// <param name="log">The log to write output to.</param>
bool Configure(TextWriter? log = null);

/// <summary>
/// Reconfigure the connections based on a new endpoint string
/// </summary>
/// <param name="endpoints">Set of new endpoints to configure with</param>
/// <param name="log">The log to write output to.</param>
Task<bool> ConfigureEndPointsAsync(string endpoints, TextWriter? log = null);

/// <summary>
/// Reconfigure the connections based on a new endpoint string
/// </summary>
/// <param name="endpoints">Comma delimited string of new endpoints to configure with</param>
/// <param name="log">The log to write output to.</param>
bool ConfigureEndPoints(string endpoints, TextWriter? log = null);

/// <summary>
/// Provides a text overview of the status of all connections.
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ StackExchange.Redis.ConnectionMultiplexer.ConfigurationChanged -> System.EventHa
StackExchange.Redis.ConnectionMultiplexer.ConfigurationChangedBroadcast -> System.EventHandler<StackExchange.Redis.EndPointEventArgs!>?
StackExchange.Redis.ConnectionMultiplexer.Configure(System.IO.TextWriter? log = null) -> bool
StackExchange.Redis.ConnectionMultiplexer.ConfigureAsync(System.IO.TextWriter? log = null) -> System.Threading.Tasks.Task<bool>!
StackExchange.Redis.ConnectionMultiplexer.ConfigureEndPoints(string! endpoints, System.IO.TextWriter? log = null) -> bool
StackExchange.Redis.ConnectionMultiplexer.ConfigureEndPointsAsync(string! endpoints, System.IO.TextWriter? log = null) -> System.Threading.Tasks.Task<bool>!
StackExchange.Redis.ConnectionMultiplexer.ConnectionFailed -> System.EventHandler<StackExchange.Redis.ConnectionFailedEventArgs!>?
StackExchange.Redis.ConnectionMultiplexer.ConnectionRestored -> System.EventHandler<StackExchange.Redis.ConnectionFailedEventArgs!>?
StackExchange.Redis.ConnectionMultiplexer.Dispose() -> void
Expand Down Expand Up @@ -461,6 +463,8 @@ StackExchange.Redis.IConnectionMultiplexer.ConfigurationChanged -> System.EventH
StackExchange.Redis.IConnectionMultiplexer.ConfigurationChangedBroadcast -> System.EventHandler<StackExchange.Redis.EndPointEventArgs!>!
StackExchange.Redis.IConnectionMultiplexer.Configure(System.IO.TextWriter? log = null) -> bool
StackExchange.Redis.IConnectionMultiplexer.ConfigureAsync(System.IO.TextWriter? log = null) -> System.Threading.Tasks.Task<bool>!
StackExchange.Redis.IConnectionMultiplexer.ConfigureEndPoints(string! endpoints, System.IO.TextWriter? log = null) -> bool
StackExchange.Redis.IConnectionMultiplexer.ConfigureEndPointsAsync(string! endpoints, System.IO.TextWriter? log = null) -> System.Threading.Tasks.Task<bool>!
StackExchange.Redis.IConnectionMultiplexer.ConnectionFailed -> System.EventHandler<StackExchange.Redis.ConnectionFailedEventArgs!>!
StackExchange.Redis.IConnectionMultiplexer.ConnectionRestored -> System.EventHandler<StackExchange.Redis.ConnectionFailedEventArgs!>!
StackExchange.Redis.IConnectionMultiplexer.ErrorMessage -> System.EventHandler<StackExchange.Redis.RedisErrorEventArgs!>!
Expand Down
5 changes: 5 additions & 0 deletions src/StackExchange.Redis/ServerSelectionStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ internal void UpdateClusterRange(int fromInclusive, int toInclusive, ServerEndPo
}
}

internal void ResetMap()
{
map = new ServerEndPoint[RedisClusterSlotCount];
}

private static unsafe int IndexOf(byte* ptr, byte value, int start, int end)
{
for (int offset = start; offset < end; offset++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ public event EventHandler<ServerMaintenanceEvent> ServerMaintenanceEvent

public Task<bool> ConfigureAsync(TextWriter? log = null) => _inner.ConfigureAsync(log);

public bool ConfigureEndPoints(string endpoints, TextWriter? log = null) => _inner.ConfigureEndPoints(endpoints, log);

public Task<bool> ConfigureEndPointsAsync(string endpoints, TextWriter? log = null) => _inner.ConfigureEndPointsAsync(endpoints, log);

public void Dispose() { } // DO NOT call _inner.Dispose();

public ValueTask DisposeAsync() => default; // DO NOT call _inner.DisposeAsync();
Expand Down