Skip to content

Commit 8719299

Browse files
committed
issue #309: obey a GapFill even if it replaces...
... a message that was processed off the queue This is relevant only during a very specific and uncommon Gap Fill situation at the end of a ResendRequest series
1 parent 48f5ad9 commit 8719299

File tree

5 files changed

+36
-10
lines changed

5 files changed

+36
-10
lines changed

AcceptanceTest/definitions/server/fix44/issue309_GapFillSkip.def

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ I8=FIX.4.435=A34=149=TW52=<TIME>56=ISLD98=0108=2
77
E8=FIX.4.435=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=210=0
88

99
# News msg to bump the seq up
10-
I8=FIX.4.435=B34=249=TW52=<TIME>56=ISLD148=no_echo33=0
10+
I8=FIX.4.435=B34=249=TW52=<TIME>56=ISLD148=no_echo
1111

1212
# Heartbeat with seq 5 (when 3 is expected)
1313
I8=FIX.4.435=034=549=TW52=<TIME>56=ISLD
@@ -16,8 +16,8 @@ I8=FIX.4.435=034=549=TW52=<TIME>56=ISLD
1616
E8=FIX.4.435=234=249=ISLD52=00000000-00:00:00.00056=TW7=316=010=0
1717

1818
# Resend 3 and 4
19-
I8=FIX.4.435=B34=343=Y49=TW52=<TIME>56=ISLD148=no_echo33=0
20-
I8=FIX.4.435=B34=443=Y49=TW52=<TIME>56=ISLD148=no_echo33=0
19+
I8=FIX.4.435=B34=343=Y49=TW52=<TIME>56=ISLD148=no_echo
20+
I8=FIX.4.435=B34=443=Y49=TW52=<TIME>56=ISLD148=no_echo
2121
sleep(0.2)
2222

2323
# Session now processes the Heartbeat (seq=5) from its queue
@@ -28,6 +28,6 @@ E8=FIX.4.435=034=349=ISLD52=00000000-00:00:00.00056=TW10=0
2828
# The EndSeqNo is 7, completely skipping over 6.
2929
I8=FIX.4.435=434=549=TW52=<TIME>56=ISLD43=Y122=<TIME-1>36=7123=Y
3030

31-
# Ensure that client obeyed the SequenceReset and set NextExpected=7
32-
I8=FIX.4.435=B34=749=TW52=<TIME>56=ISLD148=foo33=0
33-
E8=FIX.4.435=B34=449=ISLD52=00000000-00:00:00.00056=TW33=0148=foo
31+
# Do an echo to ensure that client obeyed the SequenceReset and set NextExpected=7
32+
I8=FIX.4.435=B34=749=TW52=<TIME>56=ISLD148=echo: NextExpected/7
33+
E8=FIX.4.435=B34=449=ISLD52=00000000-00:00:00.00056=TW148=NextExpected/710=0

QuickFIXn/Session.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ public void Next()
523523
public void Next(string msgStr)
524524
{
525525
NextMessage(msgStr);
526+
_state.LastProcessedMessageWasQueued = false;
526527
NextQueued();
527528
}
528529

@@ -962,8 +963,20 @@ public bool Verify(Message msg, bool checkTooHigh = true, bool checkTooLow = tru
962963
}
963964
if (checkTooLow && IsTargetTooLow(msgSeqNum))
964965
{
965-
DoTargetTooLow(msg, msgSeqNum);
966-
return false;
966+
if (_state.LastProcessedMessageWasQueued
967+
&& msg.Header.GetString(35) == MsgType.SEQUENCE_RESET
968+
&& msg.GetBoolean(Tags.GapFillFlag))
969+
{
970+
Log.Log(LogLevel.Warning,
971+
"SequenceReset-GapFill 34={MsgSeqNum} is too low (expected {NextTargetMsgSeqNum}), but in this case I'm going to obey it anyway",
972+
msgSeqNum, _state.NextTargetMsgSeqNum);
973+
// This is an uncommon situation, see #309
974+
}
975+
else
976+
{
977+
DoTargetTooLow(msg, msgSeqNum);
978+
return false;
979+
}
967980
}
968981

969982
if (IsResendRequested)
@@ -1594,6 +1607,7 @@ protected bool NextQueued(SeqNumType num)
15941607
{
15951608
NextMessage(msg.ConstructString());
15961609
}
1610+
_state.LastProcessedMessageWasQueued = true;
15971611
return true;
15981612
}
15991613
return false;

QuickFIXn/SessionState.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ public bool IsInitiator
4848

4949
public ILogger Log { get; }
5050

51+
/// <summary>
52+
/// True if the last message processed was an admin message from the queue.
53+
/// Needed for an obscure SequenceReset scenario (see issue #390).
54+
/// </summary>
55+
internal bool LastProcessedMessageWasQueued { get; set; }
56+
5157
#endregion
5258

5359
#region Synchronized Properties

RELEASE_NOTES.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ What's New
4545
* #939 - minor checkTooHigh/checkTooLow refactor in Session.cs (gbirchmeier)
4646
* #941 - clarify ResendRequest-related log message, add UT coverage for Session (gbirchmeier)
4747
* #895 - fix: When SSLCACertificate is empty an error is logged and it fails to start (dckorben)
48-
* #942 - fix #942: field 369 (LastMsgSeqNumProcessed) wrong in ResendRequest message (gbirchmeier)
48+
* #942 - fix: field 369 (LastMsgSeqNumProcessed) wrong in ResendRequest message (gbirchmeier)
4949
* #940 - Create an alternate CharEncoding.GetBytes impl which uses ArrayPool to improve memory performance (VAllens)
5050
* #951 - fix: restore Session disconnect during SocketInitiatorThread.Read exception (gbirchmeier/trevor-bush)
5151
* #963 - fix: concurrency bug with NonSessionLog on Windows (gbirchmeier)
@@ -57,6 +57,8 @@ What's New
5757
* #969 - correct LinesOfText in DDs to not be required; make ATs not auto-echo News (gbirchmeier)
5858
* #965 - Reusing StringBuilder with Object Pooling (VAllens)
5959
* #980 - fix: ToJSON returns invalid json when content contains newlines/tabs/etc (Rob-Hague)
60+
* #309 - fix: obey a SeqReset-GapFill even if it 'replaces' a message that was processed off
61+
queue in a ResendRequest series (gbirchmeier/oclancy)
6062

6163
### v1.13.1
6264
* backport #951 to 1.13

UnitTests/SessionTest.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,8 +821,11 @@ public void TestSequenceResetDuringResendRequestIsProcessed()
821821
}
822822

823823
[Test]
824-
public void TestGapFillShouldNotBeIgnoredIfPossDup()
824+
public void TestObeyGapFillIfItReplacesAMessageOffTheQueue()
825825
{
826+
// issue #309: If GapFill has the same seqno of a message that was
827+
// processed off the queue, obey it anyway
828+
826829
Assert.That(_session!.IgnorePossDupResendRequests, Is.EqualTo(false));
827830
_session.RequiresOrigSendingTime = false; // default is true
828831
Logon();
@@ -838,6 +841,7 @@ public void TestGapFillShouldNotBeIgnoredIfPossDup()
838841
Assert.That(resendRequest.EndSeqNo.Value, Is.EqualTo(0));
839842

840843
Assert.That(_session.NextTargetMsgSeqNum, Is.EqualTo(3));
844+
Assert.That(_session.IsResendRequested);
841845

842846
// Resends
843847
SendNOSMessage(3, possDupFlag: true);

0 commit comments

Comments
 (0)