QWFIX uses event model in message handling. Each FIXSession is associated with a list of IFIXEngineMessageHandler. The following is the definition of IFIXEngineMessageHandler:
public interface IFIXEngineMessageHandler { void OnMessageSent(FIXMessage message, FIXEngineMessageHandlerStatus status); void OnMessageReceived(FIXMessage message, FIXEngineMessageHandlerStatus status); }
IFIXEngineMessageHandler is a simple interface. There are only two methods defined in the interface, OnMessageSent and OnMessageReceived. Just as suggested by their names, OnMessageSent will be called when a message is sent (see note below). OnMessageReceived is called when a message is received.
Note: OnMessageSent will only be triggered during recovery, in hot standby backup server or remote synchronization applications. It won't be triggered when FIXSession.SendMessage is called.
Methods in IFIXEngineMessageHandler also take a "status" parameter of FIXEngineMessageHandlerStatus enumeration type, which indicates the current system status. Below is the definition of FIXEngineMessageHandlerStatus:
public enum FIXEngineMessageHandlerStatus { /// <summary> /// System is under normal production condition. /// </summary> Normal, /// <summary> /// System is running in a hot standby backup mode. Message is synchronized with the primary production server. /// </summary> Backup, /// <summary> /// System process has experienced a failure and the process has been restarted. System is trying to recover to the previous state by /// "replaying" all the messages communicated in order. /// </summary> Recovery, /// <summary> /// System is running in remote cache mode (e.g. remote real time order management). The message is synchronized from the primary production server. /// </summary> RemoteCache, }
Programmer should use FIXEngine.SetEngineMessageHandler to set the customized message handler for a specific FIX session. The FIXEngine.SetEngineMessageHandler takes a FIX session and a list of message type strings. Multiple handlers can be used on one single FIX session, with each handler for a different set of message types.
Default Message Handler
During run time, when a FIX message needs to be processed, the run time will try to get the handler by both the "MsgType" of the message and the session. If no handler is specified for a specific message type, a "default handler" can be used. Default handler can be set by calling FIXEngine.SetEngineMessageDefaultHandler.
If no handler for a specific message type is found, and the default handler is not set either, the message is simply ignored. No error will be reported. It is recommended that developers should always set the default handler.
In QWFIX, FIX messages must be process sequentially. That is the safe way, and the only correct way to process FIX messages.
While complying with the sequential message processing rule, QWFIX takes every possible measure to achieve maximum parallelism.
QWFIX session message handling is thread safe. Multiple threads can make SendMessage calls simultenously.
Global Message Log
QWFIX even keeps the sequence of the entire FIX message communication, in the MessageLog property. MessageLog is a collection of the entire FIX application message communication (both incoming and outgoing) keep in exactly the order in which they were sent or received.
Messages in MessageLog are application messages plus "Session Reject" messages. Administrative messages other than "Session Reject" messages are excluded from log, such as Logon and Logout messages, Heartbeat messages etc.
MessageLog is important in recovery and remote synchronization. If the application is accidentally terminated during normal trading hours, the application can be restarted. After the application is restarted, the application can perform "recovery" by "replaying" the historical FIX message communications in exactly the sequence kept in MessageLog. The rate of recovery can be over 100000 FIX message per second.
Remote synchronization is used in our realtime monitoring tool "QWFIX RTAnalyzer" or other custom user application. The FIX communications are reconstructed remotely by synchrnoizing FIX messages from MessageLog of the FIX engine. The data can be visually presented by "FIX Log Viewer", or programatically analyzed by user's own program.
QWFIX can be used to build a variety of applications. We defined several FIXEngineRole to represent different types of application scenarios:
public enum FIXEngineRole { /// <summary> /// The FIX engine is running in primary production mode. /// </summary> Primary, /// <summary> /// The FIX engine is running in hot standby backup mode. /// </summary> Backup, /// <summary> /// The FIX engine is running in remote cache mode. /// </summary> RemoteCache, /// <summary> /// The FIX Engine is searching the local historical log files. All persistence should be running in read only mode. /// </summary> LocalLogSearch, }
Primary, Backup and RemoteCache are currently implemented. More roles may be added in the future.
Primary, Backup are used in hot standby scenario, where one server is the primary production server and another one is the hot standby backup. Both servers are connected with a high speed low latency interconnect hardware (such as Infiniband or myrinet). Message communications are synchronized from primary server to backup server on a transactional basis.
Note "Hot Standby" and "Recovery" are different, altough they are both part of fail over measures. When the server process crashes, if the server has a "Hot Standby" node, that node may be switch to production mode immediately. The service will be less disrupted in that way. However if the server dos not have a "Hot Standby" node, the service process will be restarted and "Recovery" will be performed to reconstruct the business logic.
RemoteCache is used in remote synchronization application such as our real-time monitoring tool "RTAnalyzer", or user designed applications. The difference between a RemoteCache and Hot Standby application is that they have different requirement for "quality of service".
IFIXEngineMessageHandler is a simple interface. Along with the "status" parameter and FIXEngine.EngineRole property, QWFIX is able to handle all application scenarios with that simple interface.
First of all, application can send FIX message through a FIX session by calling FIXSession.SendMessage, from any thread.
When the QWFIX runtime receives a FIX message, OnMessageReceived will be triggered. The FIXMessage passed to OnMessageReceived contains plenty of information, including Direction, MessageSchema and SessionPersistence. Other information such as FIXSession or FIXEngine can be retrieved indirectly through SessionPersistence.
Note OnMessageSent will only be triggered during recovery, in hot standby backup server or remote synchronization applications. It will not be triggered when FIXSession.SendMessage is called (because you know you sent a message already).
IFIXEngineMessageHandler allows application register different listeners for different message types. It allows multiple different applications share the same FIX session without even knowing the existence of the the others.
What if something goes wrong and the message got rejected? Which message handler is going to handle the reject message? In QWFIX, once a session Reject (or BusinessMessageReject) message is received, the run-time tries to retrieve the original message that was rejected throug the value of "RefSeqNum" of the reject message. It then send the reject to the handler of the original message to process.
So expect your message handler to process "Reject" and "BusinessMessageReject" messages as well, even though they are not explicitly included.
Different Between "Reject" and "BusinessMessageReject" Message Handling
QWFIX run-time handles "Reject" and "BusinessMessageReject" messages in slightly different ways.
The outgoing "Reject" message is generated in the session level automatically. Application will never have a chance to see the incoming message that triggered the outgoing "Reject" message. So only the incoming "Reject" messages can be processed by the application.
The "BusinessMessageReject" message is generated by the application based on its business logic. Not only the incoming "BusinessMessageReject" can be processed by application code, the outgoing "BusinessMessageReject" message will be processed by the message handler during the recovery time as well.