Skip to content

Commit

Permalink
Support Alibaba pseudo-cluster configurations (#2646)
Browse files Browse the repository at this point in the history
* fix #2642

1: don't treat trivial clusters as clusters - Alibaba uses this config
2: report synchronous failures immidiately and accurately

* instead of using node count, use explicit tracking of the DB count

* release notes
  • Loading branch information
mgravell authored Feb 13, 2024
1 parent d9c9f7b commit 39eac01
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 17 deletions.
3 changes: 2 additions & 1 deletion docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ Current package versions:
| [![StackExchange.Redis](https://img.shields.io/nuget/v/StackExchange.Redis.svg)](https://www.nuget.org/packages/StackExchange.Redis/) | [![StackExchange.Redis](https://img.shields.io/nuget/vpre/StackExchange.Redis.svg)](https://www.nuget.org/packages/StackExchange.Redis/) | [![StackExchange.Redis MyGet](https://img.shields.io/myget/stackoverflow/vpre/StackExchange.Redis.svg)](https://www.myget.org/feed/stackoverflow/package/nuget/StackExchange.Redis) |

## Unreleased
No unreleased changes

- Fix [#2642](https://github.com/StackExchange/StackExchange.Redis/issues/2642): Detect and support multi-DB pseudo-cluster/proxy scenarios ([#2646](https://github.com/StackExchange/StackExchange.Redis/pull/2646) by mgravell)

## 2.7.17

Expand Down
33 changes: 18 additions & 15 deletions src/StackExchange.Redis/ConnectionMultiplexer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
using Microsoft.Extensions.Logging;
using Pipelines.Sockets.Unofficial;
using StackExchange.Redis.Profiling;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
Expand All @@ -11,9 +14,6 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Pipelines.Sockets.Unofficial;
using StackExchange.Redis.Profiling;

namespace StackExchange.Redis
{
Expand Down Expand Up @@ -2078,19 +2078,22 @@ internal static void ThrowFailed<T>(TaskCompletionSource<T>? source, Exception u
#pragma warning disable CS0618 // Type or member is obsolete
result = TryPushMessageToBridgeSync(message, processor, source, ref server);
#pragma warning restore CS0618
if (result != WriteResult.Success)
if (!source.IsFaulted) // if we faulted while writing, we don't need to wait
{
throw GetException(result, message, server);
}
if (result != WriteResult.Success)
{
throw GetException(result, message, server);
}

if (Monitor.Wait(source, TimeoutMilliseconds))
{
Trace("Timely response to " + message);
}
else
{
Trace("Timeout performing " + message);
timeout = true;
if (Monitor.Wait(source, TimeoutMilliseconds))
{
Trace("Timely response to " + message);
}
else
{
Trace("Timeout performing " + message);
timeout = true;
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/StackExchange.Redis/PhysicalConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ public PhysicalConnection(PhysicalBridge bridge)
OnCreateEcho();
}

// *definitely* multi-database; this can help identify some unusual config scenarios
internal bool MultiDatabasesOverride { get; set; } // switch to flags-enum if more needed later

internal async Task BeginConnectAsync(ILogger? log)
{
var bridge = BridgeCouldBeNull;
Expand Down Expand Up @@ -262,6 +265,7 @@ private enum ReadMode : byte

private RedisProtocol _protocol; // note starts at **zero**, not RESP2
public RedisProtocol? Protocol => _protocol == 0 ? null : _protocol;

internal void SetProtocol(RedisProtocol value) => _protocol = value;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
Expand Down
11 changes: 10 additions & 1 deletion src/StackExchange.Redis/ResultProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,10 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
int dbCount = checked((int)i64);
Log?.LogInformation($"{Format.ToString(server)}: Auto-configured (CONFIG) databases: " + dbCount);
server.Databases = dbCount;
if (dbCount > 1)
{
connection.MultiDatabasesOverride = true;
}
}
else if (key.IsEqual(CommonReplies.slave_read_only) || key.IsEqual(CommonReplies.replica_read_only))
{
Expand Down Expand Up @@ -1108,8 +1112,13 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
case ResultType.BulkString:
string nodes = result.GetString()!;
var bridge = connection.BridgeCouldBeNull;
if (bridge != null) bridge.ServerEndPoint.ServerType = ServerType.Cluster;
var config = Parse(connection, nodes);

// re multi-db: https://github.com/StackExchange/StackExchange.Redis/issues/2642
if (bridge != null && !connection.MultiDatabasesOverride)
{
bridge.ServerEndPoint.ServerType = ServerType.Cluster;
}
SetResult(message, config);
return true;
}
Expand Down
4 changes: 4 additions & 0 deletions src/StackExchange.Redis/ServerEndPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ internal async Task AutoConfigureAsync(PhysicalConnection? connection, ILogger?
lastInfoReplicationCheckTicks = Environment.TickCount;
if (features.InfoSections)
{
// note: Redis 7.0 has a multi-section usage, but we don't know
// the server version at this point; we *could* use the optional
// value on the config, but let's keep things simple: these
// commands are suitably cheap
msg = Message.Create(-1, flags, RedisCommand.INFO, RedisLiterals.replication);
msg.SetInternalCall();
await WriteDirectOrQueueFireAndForgetAsync(connection, msg, autoConfigProcessor).ForAwait();
Expand Down

0 comments on commit 39eac01

Please sign in to comment.