From f1dcd040212fc632d8eed69709a5cb59993e0d92 Mon Sep 17 00:00:00 2001 From: MartinAdams Date: Sun, 5 Jul 2015 23:44:35 +0100 Subject: [PATCH 1/3] Fix incorrect sequencing of gap fill messages generated on re-send. --- QuickFIXn/Session.cs | 16 ++++++---- UnitTests/SessionTest.cs | 64 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/QuickFIXn/Session.cs b/QuickFIXn/Session.cs index cb5262f1e..b26f7fa61 100755 --- a/QuickFIXn/Session.cs +++ b/QuickFIXn/Session.cs @@ -793,11 +793,6 @@ protected void NextResendRequest(Message resendReq) current = msgSeqNum + 1; } - if (begin != 0) - { - GenerateSequenceReset(resendReq, begin, msgSeqNum + 1); - } - if (endSeqNo > msgSeqNum) { endSeqNo = endSeqNo + 1; @@ -806,7 +801,16 @@ protected void NextResendRequest(Message resendReq) { endSeqNo = next; } - GenerateSequenceReset(resendReq, begSeqNo, endSeqNo); + } + + if (begin == 0) + { + begin = current; + } + + if (endSeqNo > begin) + { + GenerateSequenceReset(resendReq, begin, endSeqNo); } } msgSeqNum = resendReq.Header.GetInt(Tags.MsgSeqNum); diff --git a/UnitTests/SessionTest.cs b/UnitTests/SessionTest.cs index 444140cc8..22e7aba66 100755 --- a/UnitTests/SessionTest.cs +++ b/UnitTests/SessionTest.cs @@ -65,6 +65,11 @@ public void DumpMsgLookup() } } + public int GetCount(string msgType) + { + return msgLookup.ContainsKey(msgType) ? msgLookup[msgType].Count : 0; + } + #endregion } @@ -432,6 +437,65 @@ public void NextResendRequestNoMessagePersist() Assert.IsFalse(RESENT()); } + [Test] + public void TestGapFillOnResend() + { + // Engineer a gap fill at the beginning of a re-send range, in the middle, and at the end + Logon(); //seq 1 + QuickFix.FIX42.NewOrderSingle order = new QuickFix.FIX42.NewOrderSingle( + new QuickFix.Fields.ClOrdID("1"), + new QuickFix.Fields.HandlInst(QuickFix.Fields.HandlInst.MANUAL_ORDER), + new QuickFix.Fields.Symbol("IBM"), + new QuickFix.Fields.Side(QuickFix.Fields.Side.BUY), + new QuickFix.Fields.TransactTime(), + new QuickFix.Fields.OrdType(QuickFix.Fields.OrdType.LIMIT)); + + order.Header.SetField(new QuickFix.Fields.TargetCompID(sessionID.TargetCompID)); + order.Header.SetField(new QuickFix.Fields.SenderCompID(sessionID.SenderCompID)); + + int[] gapStarts = new[] { 1, 5, 11 }; // 1st gap from seq num 1 to 2 is just the Logon message + int[] gapEnds = new[] { 2, 8, 15 }; + int orderCount = 0; + + for (int msgSeqNum = gapEnds[0]; msgSeqNum < gapStarts[1]; ++msgSeqNum) + { + order.Header.SetField(new QuickFix.Fields.MsgSeqNum(msgSeqNum)); + session.Send(order); + ++orderCount; + } //seq 4, next is 5 + + for (int msgSeqNum = gapStarts[1]; msgSeqNum < gapEnds[1]; ++msgSeqNum) + { + session.GenerateHeartbeat(); + } //seq 7, next is 8 + + for (int msgSeqNum = gapEnds[1]; msgSeqNum < gapStarts[2]; ++msgSeqNum) + { + order.Header.SetField(new QuickFix.Fields.MsgSeqNum(msgSeqNum)); + session.Send(order); + ++orderCount; + } //seq 10, next is 11 + + for (int msgSeqNum = gapStarts[2]; msgSeqNum < gapEnds[2]; ++msgSeqNum) + { + session.GenerateHeartbeat(); + } // seq 11 - 14 + + responder.msgLookup.Clear(); + SendResendRequest(1, 100); + + Assert.AreEqual(responder.GetCount(QuickFix.Fields.MsgType.NEWORDERSINGLE), orderCount); + Assert.AreEqual(responder.GetCount(QuickFix.Fields.MsgType.SEQUENCE_RESET), gapStarts.Length); + + int count = -1; + foreach (QuickFix.Message sequenceResestMsg in responder.msgLookup[QuickFix.Fields.MsgType.SEQUENCE_RESET]) + { + Assert.AreEqual(sequenceResestMsg.GetField(QuickFix.Fields.Tags.GapFillFlag), "Y"); + Assert.AreEqual(sequenceResestMsg.Header.GetInt(QuickFix.Fields.Tags.MsgSeqNum), gapStarts[++count]); + Assert.AreEqual(sequenceResestMsg.GetInt(QuickFix.Fields.Tags.NewSeqNo), gapEnds[count]); + } + } + public void AssertMsInTag(string msgType, int tag, bool shouldHaveMs) { QuickFix.Message msg = responder.msgLookup[msgType].Last(); From ccfb92a5a8de2c725a9809d6a6206948cf2f9d37 Mon Sep 17 00:00:00 2001 From: MartinAdams Date: Mon, 6 Jul 2015 01:25:25 +0100 Subject: [PATCH 2/3] Further work on incorrect sequencing of gap fill messages --- QuickFIXn/Session.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/QuickFIXn/Session.cs b/QuickFIXn/Session.cs index b26f7fa61..cde999fb4 100755 --- a/QuickFIXn/Session.cs +++ b/QuickFIXn/Session.cs @@ -793,14 +793,10 @@ protected void NextResendRequest(Message resendReq) current = msgSeqNum + 1; } - if (endSeqNo > msgSeqNum) + int nextSeqNum = state_.GetNextSenderMsgSeqNum(); + if (++endSeqNo > nextSeqNum) { - endSeqNo = endSeqNo + 1; - int next = state_.GetNextSenderMsgSeqNum(); - if (endSeqNo > next) - { - endSeqNo = next; - } + endSeqNo = nextSeqNum; } if (begin == 0) From f37a724691b823368c4dba791a8ddc34ad8b091c Mon Sep 17 00:00:00 2001 From: MartinAdams Date: Tue, 7 Jul 2015 22:34:36 +0100 Subject: [PATCH 3/3] Add change entry --- NEXT_VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEXT_VERSION.md b/NEXT_VERSION.md index 76431acff..8698b2024 100644 --- a/NEXT_VERSION.md +++ b/NEXT_VERSION.md @@ -32,4 +32,4 @@ Changes since the last version (oldest first): * (patch) #80 - fixes to tag-141-related sequence resets (TomasVetrovsky,akamyshanov,gbirchmeier) * (patch) #315 - make config file section headers be case-insensitive, for parity with QF/j (gbirchmeier) * (minor) #314 - New feature: add/remove sessions dynamically (martinadams) - +* (patch) #278 - fix for mis-sequenced gap fills in re-requested messages (martinadams)