File-Based Session Storage keeps messages in set of files arranged in directory, and specified by OnixS::FIX::EngineSettings::logDirectory settings. This storage usually keeps data at two kinds of files: messages are kept in StorageId.summary and session state kept in StorageId.state file. StorageID is a generic name of storage and it is available through the OnixS::FIX::Session::storageId() method.
In particular cases, it is needed to save space on disk and there is no need to keep the whole history of incoming and outgoing messages. To satisfy such requirements, File-Based Storage could be configured to store messages in a set of .summary files, instead of a single one. The entire set of logged messages is split through several files called "segments". For removal guidance, see Removing old segments.
For a comparison of recovery and replay guarantees, see Selecting Storage for Session Messages Logging. Before restarting or resetting a session, follow Restoring Session State from Log Files.
Segmented File-Based Storage
Segmented mode of file-based storage is initiated when the OnixS::FIX::EngineSettings::fileBasedStorageSegmentSize setting is configured to non-zero before Engine startup. This value specifies the maximum size of each one file where incoming and outgoing messages stored and affects all sessions created.
Names of segments composed as: StorageID[-part-N].summary, where:
- StorageID - generic name of storage, available through the OnixS::FIX::Session::storageId() method;
- -part-N - suffix of the part. The N is 1-based number of the part. One segment does not have this suffix - it is the segment where freshmost messages are written.
During message interchange, files are created and fulfilled by the following common algorithm:
- The new message is always logged to StorageID.summary file;
- After the new message logged, StorageID.summary file size is compared to the EngineSettings::fileBasedStorageSegmentSize value, and if the actual file size exceeds, a value new segment is created;
- To create new segment, the Engine renames StorageID.summary to StorageID-part-N.summary and creates a new empty StorageID.summary file to log subsequent messages. The N is a sequential number of part starting from 1.
So during a long session, there is a set of files created: StorageID.summary, StorageID-part-1.summary, StorageID-part-2.summary, etc. Oldest messages are arranged at StorageID-part-1.summary, and the freshmost ones - at StorageID.summary and StorageID-part-N.summary, where N is of maximum value.
At the same time, the entire number of parts is registered at StorageID.state with the key of FilePartCount. For instance, if there are 5 parts (StorageID.summary and StorageID-part-1.summary - StorageID-part-4.summary) the following line will be written in StorageID.state:
FilePartCount=5
On restart, the Engine rebuilds the resend queue by reading segments from newest to oldest:
- Read FilePartCount from StorageID.state.
- Read the newest segment, StorageID.summary.
- If more messages are needed, read StorageID-part-N.summary in descending order from FilePartCount - 1 to 1.
Removing old segments
Required segments are StorageID.summary plus the highest-numbered parts, with no gaps. How many parts are required depends on the global OnixS::FIX::EngineSettings::resendingQueueSize (see Resending queue size) and the messages they contain. Increasing that setting later can make older parts necessary.
The Engine does not delete old parts automatically. To free disk space:
- Stop the Engine. Move one or more of the oldest parts out of the storage directory, starting with StorageID-part-1.summary.
- Start the Engine with a nonzero OnixS::FIX::EngineSettings::fileBasedStorageSegmentSize, the intended global resendingQueueSize, and OnixS::FIX::EngineSettings::ignoreFileBasedStorageIntegrityErrors set to false, so a missing required part makes session creation throw instead of creating new storage (see Checking integrity of segmented File-Based Storage). Recreate the session without connecting. Check that the original StorageID and sequence numbers were restored; see Before reconnecting.
- If session creation throws or the restored state differs, stop the Engine and put the moved parts back before investigating or trying fewer.
- If the session is created with the expected state, the moved parts are not needed to rebuild the resend queue with those settings. Keep a backup if you need their message history or may increase resendingQueueSize later; otherwise, delete them to free space.
Combinations between segmented and non-segmented File-Based Storages
It is important to understand that Engine decides whether the storage working mode is just on the startup. Assume the following situation:
- Configure EngineSettings::fileBasedStorageSegmentSize to non-zero and start Engine;
- Start the session which has a lot of exchanges, and creates, for instance, 5 segments;
- Stop Engine;
- Configure EngineSettings::fileBasedStorageSegmentSize to 0, start Engine;
- Start the same sessions as above, trying to restore session state from the existing storage. The Engine will read FilePartCount=5, but it will count that there is no upper limit for segment size. Therefore, messages still will be read from (possibly) all available segments. But during the session activity, no more segments will be created, and StorageID.summary file will grow without limit. From some moments, this file will carry all messages required for the resend request, and -part-N.summary files will not be needed.
Also, assume another situation.
- Configure EngineSettings::fileBasedStorageSegmentSize to 0 and start Engine;
- Start the session which has a lot of exchanges, and creates big single segment with the name of StorageID.summary;
- Stop Engine;
- Configure EngineSettings::fileBasedStorageSegmentSize to non-zero value which is less than the StorageID.summary size, start Engine;
- Start the same sessions as above, trying to restore a session state from the existing storage. Now the Engine will treat existing .summary as just a single big segment, which size exceeds limit of a segment size. Therefore, Engine will log just a single message to the file (that will be Logon message), which will rename the StorageID.summary to StorageID-part-1.summary, and will create new StorageID.summary to log subsequent messages.
Checking integrity of segmented File-Based Storage
When a session is created from existing segmented storage, the Engine checks that it can read every segment needed to rebuild the resend queue. If a required segment is missing or inaccessible, OnixS::FIX::EngineSettings::ignoreFileBasedStorageIntegrityErrors determines the outcome:
- false (default): Session creation throws an exception.
- true: The Engine creates new storage with a new StorageID, resets all sequence numbers to their initial values, and writes a message to the Engine log.
Attaching File-Based Storage
Examples
Segmented storage
.ignoreFileBasedStorageIntegrityErrors(true);
File-Based Storage without segmentation