TimelordUK / jspurefix

native typescript FIX engine
MIT License
58 stars 27 forks source link

Heartbeat Interval #38

Closed rcluan closed 1 year ago

rcluan commented 2 years ago

Hello. We are using this lib on a production product and we found ourselves in a quite situation with Heartbeat and TestRequest messages. We built only the client and we accessing a 3rd party server.

After checking the fix logs we noticed that the heartbeats issued by the client have always an increase on the milliseconds, whilst the server always sends the heartbeats with a fixed millisecond.

8=FIX.4.4|9=122|35=4|34=130|43=Y|49=server|50=server|52=20220510-17:57:15.917|56=client|122=20220510-17:57:15.917|36=131|123=Y|10=163|
8=FIX.4.4|9=0000112|35=0|49=client|56=server|34=124|57=server|52=20220510-17:57:43.849|112=Tue, 10 May 2022 17:57:43 GMT|10=174|
8=FIX.4.4|9=78|35=0|34=131|49=server|50=server|52=20220510-17:57:43.977|56=client|10=017|
8=FIX.4.4|9=0000112|35=0|49=client|56=server|34=125|57=server|52=20220510-17:58:11.886|112=Tue, 10 May 2022 17:58:11 GMT|10=168|
8=FIX.4.4|9=78|35=0|34=132|49=server|50=server|52=20220510-17:58:11.977|56=client|10=014|

This led us to check the code in the lib and we found out that the heartbeat is currently wrapped by a setInvertal of 200ms. Is there a specific reason for that? This interval calls this tick() method, which caculates the whether or not the application should send the heartbeat based on the last heartbeat it received and the heartbeat amount we set in the client configuration.

// ascii-session.ts
if (this.heartbeat) {
      logger.debug(`start heartbeat timer.`)
      this.startTimer()
}
// ascii-session.ts
private startTimer (interval: number = 200) {
    this.timer = setInterval(() => {
      this.tick()
    }, interval)
}
// fix-session-state.ts
public timeToHeartbeat (): boolean {
    return this.secondsSinceSent >= this.heartBeat
}

We believe that this is causing the server and client to drift in the heartbeat time control and it's making the server send a TestRequest from time to time. I believe the heartbeat should have its own interval and this interval should start right after the logon message is received by the application.

Maybe one quick way would be changing the heartbeat attribute on our custom session class and then configuring ourselves the hearbeat interval once we receive a Logon message, maybe in the onDecoded. However, if we do that we might be missing a few steps that guarantee we should start sending heartbeats. Any thoughts?

TimelordUK commented 2 years ago

thanks for raising some interesting points,

A better solution may be

  1. every time message is sent (including heartbeat) cancel existing timer, re-create a timer @ timeout seconds.
  2. if timer expires we have been inactive for N seconds, so we send heartbeat - and create heartbeat timer
  3. next message repeat 1

this will at least mean when connection is quiet we will send at regular intervals and should not slip.

Having said that I cannot guarantee it will fix your particular problem.

rcluan commented 1 year ago

@TimelordUK I'm closing this issue. It turns out we were having a network problem. I think we can, however, improve to have a constant heartbeat time, but that's not critical.