FixAlchemy.SessionStore behaviour (FIXAlchemy v0.2.2)
View SourceStorage for a session's sequence numbers, keyed by session identity rather than by process.
A session that must resume its numbering after a reconnect, a restart, or a crash loads it from here at logon and reports every number it uses. The identity is the FIX session key — BeginString, SenderCompID, TargetCompID — so the same session finds its own numbers whichever process is running it.
Sessions have no store unless one is configured, in which case numbering starts at 1 on every connection and nothing is written.
FixAlchemy.Server.start_link(
session_store: FixAlchemy.SessionStore.Ets,
session_store_opts: [table: :my_fix_sequences],
...
)Rules an implementation must follow
record_outbound/2andrecord_inbound/2are called once for every message the session sends and accepts. What is written, and when, is the implementation's choice; the engine does not wait on either call.load/1may return a number higher than any the session actually used. It must never return a lower one — the counterparty rejects a MsgSeqNum below what it expects, and the session ends.- A gap between what
load/1returns and the last number used is answered by the session with SequenceReset-GapFill, and needs nothing from the implementation. close/1is the only point at which anything held back is guaranteed to be flushed. It is not called when the node dies.
Implementing
defmodule MyApp.CubSessionStore do
@behaviour FixAlchemy.SessionStore
@impl true
def open(key, opts), do: {:ok, {Keyword.fetch!(opts, :db), key}}
@impl true
def load({db, key}) do
CubDB.get(db, key, %{next_outbound: 1, expected_inbound: 1})
end
@impl true
def record_outbound({db, key} = handle, seq) do
CubDB.put(db, key, %{load(handle) | next_outbound: seq + 1})
end
# record_inbound/2, reset/1 and close/1 likewise
end
Summary
Types
Whatever open/2 returns; passed back to every other callback.
BeginString, SenderCompID, TargetCompID.
Callbacks
Flush anything held and release the handle. Called when the session ends.
The numbers this session resumes from.
Open storage for one session. Called once, before the session sends anything.
Record that seq has been accepted from the counterparty.
Record that seq has been assigned to an outbound message.
Return both directions to 1, as a Logon with ResetSeqNumFlag requires.
Types
@type handle() :: term()
Whatever open/2 returns; passed back to every other callback.
BeginString, SenderCompID, TargetCompID.
@type sequences() :: %{next_outbound: pos_integer(), expected_inbound: pos_integer()}
Callbacks
@callback close(handle()) :: :ok
Flush anything held and release the handle. Called when the session ends.
The numbers this session resumes from.
Returns %{next_outbound: 1, expected_inbound: 1} for a session never seen
before.
Open storage for one session. Called once, before the session sends anything.
@callback record_inbound(handle(), seq :: pos_integer()) :: :ok
Record that seq has been accepted from the counterparty.
@callback record_outbound(handle(), seq :: pos_integer()) :: :ok
Record that seq has been assigned to an outbound message.
@callback reset(handle()) :: :ok
Return both directions to 1, as a Logon with ResetSeqNumFlag requires.
Functions
@spec initial() :: sequences()
The numbers a session starts from when it has never been stored.