Problem
At session start the gateway replays recent executions. Each replayed message arrives twice — once inside a FIXCOMP envelope and once as plain FIX — carrying the same MsgSeqNum and the same ExecID:
WIRE< ccp/comp 8=FIX.4.1|9=000457|35=8|34=000007|43=N|97=Y|11=9001.0|17=00025b49.6a706c96.01.01|150=2|20=0|39=2|32=1|31=737.53
WIRE< ccp/fix 8=FIX.4.1|9=000457|35=8|34=000007|43=N|97=Y|11=9001.0|17=00025b49.6a706c96.01.01|150=2|20=0|39=2|32=1|31=737.53
Byte-identical, both reaching the handler. Counting inbound execution reports over one paper session, the replay batch is doubled and this session's own reports are not:
2 35=8|34=000007 ← replay
2 35=8|34=000009 ← replay
2 35=8|34=000011 ← replay
2 35=8|34=000013 ← replay
1 35=8|34=000118 ← this session
1 35=8|34=000119
1 35=8|34=000120
The handler has no MsgSeqNum-based deduplication. The only thing preventing the second copy from being acted on is the ExecID window, and that covers the fill block alone — status transitions, the order and contract cache enrichment, and the execution-detail callbacks all run again on the second copy.
The ExecID window is also bounded at 1024 entries. A replay batch larger than the window, or one interleaved with enough live executions, evicts its own earlier entries and the fill block itself becomes reachable twice.
Impact
Duplicated callbacks for the same execution. Anything counting callbacks rather than reconciling state sees each replayed execution twice.
The narrow escape is that these carry 97=Y, so a handler that consulted the resend marker would reject the second copy — but neither 97 nor 43 is read anywhere in the handler today, so nothing does.
Solution
Deduplicate on MsgSeqNum for inbound messages on this socket. FIX carries the field for exactly this, it is already parsed, and it covers every message type rather than only the ones that happen to have an ExecID.
Consulting the PossResend marker (97) would also cover this particular case, and is worth doing regardless — a replayed execution restates history rather than reporting something new, and a fresh process has no ExecID memory at all.
Problem
At session start the gateway replays recent executions. Each replayed message arrives twice — once inside a FIXCOMP envelope and once as plain FIX — carrying the same MsgSeqNum and the same ExecID:
Byte-identical, both reaching the handler. Counting inbound execution reports over one paper session, the replay batch is doubled and this session's own reports are not:
The handler has no MsgSeqNum-based deduplication. The only thing preventing the second copy from being acted on is the ExecID window, and that covers the fill block alone — status transitions, the order and contract cache enrichment, and the execution-detail callbacks all run again on the second copy.
The ExecID window is also bounded at 1024 entries. A replay batch larger than the window, or one interleaved with enough live executions, evicts its own earlier entries and the fill block itself becomes reachable twice.
Impact
Duplicated callbacks for the same execution. Anything counting callbacks rather than reconciling state sees each replayed execution twice.
The narrow escape is that these carry
97=Y, so a handler that consulted the resend marker would reject the second copy — but neither97nor43is read anywhere in the handler today, so nothing does.Solution
Deduplicate on MsgSeqNum for inbound messages on this socket. FIX carries the field for exactly this, it is already parsed, and it covers every message type rather than only the ones that happen to have an ExecID.
Consulting the PossResend marker (97) would also cover this particular case, and is worth doing regardless — a replayed execution restates history rather than reporting something new, and a fresh process has no ExecID memory at all.