@@ -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