Skip to content

Commit ed0e353

Browse files
authored
Track the number of live multiplexers (#3030)
* Track the number of live multiplexers, and report it in the error dump if non-trivial * fight the CI on a CS9336 mixup * simplify counting
1 parent bc086f3 commit ed0e353

File tree

4 files changed

+28
-4
lines changed

4 files changed

+28
-4
lines changed

src/StackExchange.Redis/ConnectionMultiplexer.Debug.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,18 @@ namespace StackExchange.Redis;
44

55
public partial class ConnectionMultiplexer
66
{
7-
private static int _collectedWithoutDispose;
7+
private static int _collectedWithoutDispose, s_DisposedCount, s_MuxerCreateCount;
88
internal static int CollectedWithoutDispose => Volatile.Read(ref _collectedWithoutDispose);
99

10+
internal static int GetLiveObjectCount(out int created, out int disposed, out int finalized)
11+
{
12+
// read destroy first, to prevent negative numbers in race conditions
13+
disposed = Volatile.Read(ref s_DisposedCount);
14+
created = Volatile.Read(ref s_MuxerCreateCount);
15+
finalized = Volatile.Read(ref _collectedWithoutDispose);
16+
return created - (disposed + finalized);
17+
}
18+
1019
/// <summary>
1120
/// Invoked by the garbage collector.
1221
/// </summary>

src/StackExchange.Redis/ConnectionMultiplexer.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ static ConnectionMultiplexer()
128128

129129
private ConnectionMultiplexer(ConfigurationOptions configuration, ServerType? serverType = null, EndPointCollection? endpoints = null)
130130
{
131+
Interlocked.Increment(ref s_MuxerCreateCount);
132+
131133
RawConfig = configuration ?? throw new ArgumentNullException(nameof(configuration));
132134
EndPoints = endpoints ?? RawConfig.EndPoints.Clone();
133135
EndPoints.SetDefaultPorts(serverType, ssl: RawConfig.Ssl);
@@ -2258,7 +2260,8 @@ ConfigurationChangedChannel is byte[] channel
22582260
public void Dispose()
22592261
{
22602262
GC.SuppressFinalize(this);
2261-
Close(!_isDisposed);
2263+
if (!_isDisposed) Interlocked.Increment(ref s_DisposedCount);
2264+
Close(!_isDisposed); // marks disposed
22622265
sentinelConnection?.Dispose();
22632266
var oldTimer = Interlocked.Exchange(ref sentinelPrimaryReconnectTimer, null);
22642267
oldTimer?.Dispose();
@@ -2270,7 +2273,8 @@ public void Dispose()
22702273
public async ValueTask DisposeAsync()
22712274
{
22722275
GC.SuppressFinalize(this);
2273-
await CloseAsync(!_isDisposed).ForAwait();
2276+
if (!_isDisposed) Interlocked.Increment(ref s_DisposedCount);
2277+
await CloseAsync(!_isDisposed).ForAwait(); // marks disposed
22742278
if (sentinelConnection is ConnectionMultiplexer sentinel)
22752279
{
22762280
await sentinel.DisposeAsync().ForAwait();

src/StackExchange.Redis/ExceptionFactory.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,12 @@ private static void AddCommonDetail(
347347
Add(data, sb, "Last-Result-Bytes", "last-in", bs.Connection.BytesLastResult.ToString());
348348
Add(data, sb, "Inbound-Buffer-Bytes", "cur-in", bs.Connection.BytesInBuffer.ToString());
349349

350+
var liveMuxers = ConnectionMultiplexer.GetLiveObjectCount(out var created, out var disposed, out var finalized);
351+
if (created > 1)
352+
{
353+
Add(data, sb, "Live-Multiplexers", "lm", $"{liveMuxers}/{created}/{disposed}/{finalized}");
354+
}
355+
350356
Add(data, sb, "Sync-Ops", "sync-ops", multiplexer.syncOps.ToString());
351357
Add(data, sb, "Async-Ops", "async-ops", multiplexer.asyncOps.ToString());
352358

src/StackExchange.Redis/PhysicalBridge.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1622,8 +1622,13 @@ private WriteResult WriteMessageToServerInsideWriteLock(PhysicalConnection conne
16221622

16231623
if (_nextHighIntegrityToken is not 0
16241624
&& !connection.TransactionActive // validated in the UNWATCH/EXEC/DISCARD
1625-
&& message.Command is not RedisCommand.AUTH or RedisCommand.HELLO) // if auth fails, ECHO may also fail; avoid confusion
1625+
&& message.Command is not RedisCommand.AUTH // if auth fails, later commands may also fail; avoid confusion
1626+
&& message.Command is not RedisCommand.HELLO)
16261627
{
1628+
// note on the Command match above: curiously, .NET 10 and .NET 11 SDKs emit *opposite* errors here
1629+
// re "CS9336: The pattern is redundant." ("fixing" one "breaks" the other); possibly a fixed bool inversion
1630+
// in the analyzer? to avoid pain, we'll just use the most obviously correct form
1631+
16271632
// make sure this value exists early to avoid a race condition
16281633
// if the response comes back super quickly
16291634
message.WithHighIntegrity(NextHighIntegrityTokenInsideLock());

0 commit comments

Comments
 (0)