Skip to content

Commit a361ade

Browse files
committed
upstream #3049 into v3
1 parent 1bad109 commit a361ade

File tree

2 files changed

+25
-6
lines changed

2 files changed

+25
-6
lines changed

docs/ReleaseNotes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Current package versions:
88

99
## Unreleased
1010

11-
- none
11+
- Critical fix for high-integrity mode in cluster scenarios ([#3049 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/issues/3049))
1212

1313
## 2.12.8
1414

src/StackExchange.Redis/PhysicalConnection.Read.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -642,11 +642,16 @@ static void Throw(ReadOnlySpan<byte> frame, ConnectionType connection, RedisProt
642642
var reader = new RespReader(frame);
643643

644644
OnDetailLog($"computing result for {msg.CommandAndKey} ({RespReaderExtensions.GetRespPrefix(frame)})");
645-
if (msg.ComputeResult(this, ref reader))
645+
646+
// need to capture HIT promptly, as -MOVED could cause a resend with a new high-integrity token
647+
// (a lazy approach would be to not rotate, but: we'd rather avoid that; the -MOVED case is rare)
648+
var highIntegrityToken = msg.HighIntegrityToken;
649+
bool computed = msg.ComputeResult(this, ref reader);
650+
if (computed)
646651
{
647652
OnDetailLog($"> complete: {msg.CommandAndKey}");
648653
_readStatus = msg.ResultBoxIsAsync ? ReadStatus.CompletePendingMessageAsync : ReadStatus.CompletePendingMessageSync;
649-
if (!msg.IsHighIntegrity)
654+
if (highIntegrityToken is 0)
650655
{
651656
// can't complete yet if needs checksum
652657
msg.Complete();
@@ -656,10 +661,10 @@ static void Throw(ReadOnlySpan<byte> frame, ConnectionType connection, RedisProt
656661
{
657662
OnDetailLog($"> incomplete: {msg.CommandAndKey}");
658663
}
659-
if (msg.IsHighIntegrity)
664+
if (highIntegrityToken is not 0)
660665
{
661-
// stash this for the next non-OOB response
662-
Volatile.Write(ref _awaitingToken, msg);
666+
// stash this for the next non-OOB response, retaining the old HIT iff we had a -MOVED etc
667+
Volatile.Write(ref _awaitingToken, computed ? msg : new DummyHighIntegrityMessage(msg, highIntegrityToken));
663668
}
664669

665670
_readStatus = ReadStatus.MatchResultComplete;
@@ -787,4 +792,18 @@ private static void DebugValidateSingleFrame(ReadOnlySpan<byte> payload)
787792
$"Unexpected additional {reader.ProtocolBytesRemaining} bytes remaining, {prefix}");
788793
}
789794
}
795+
796+
internal sealed class DummyHighIntegrityMessage : Message
797+
{
798+
// note: we don't create this message very often - only when a HIT gets a -MOVED or similar
799+
public DummyHighIntegrityMessage(Message msg, uint highIntegrityToken) : base(msg.Db, msg.Flags, msg.Command)
800+
{
801+
WithHighIntegrity(highIntegrityToken);
802+
}
803+
804+
public override int ArgCount => 0;
805+
806+
protected override void WriteImpl(in MessageWriter writer)
807+
=> throw new NotSupportedException("This message cannot be written; it is a place-holder for high-integrity scenarios.");
808+
}
790809
}

0 commit comments

Comments
 (0)