FixAlchemy.Sequence (FIXAlchemy v0.2.2)

View Source

Session sequence-number state for one FIX connection, both directions.

Owns the outbound MsgSeqNum counter and, for sessions that enable :sequence_recovery, the inbound state machine: whether an arriving message is in order, opens a gap, is a duplicate, or is fatally low. Messages that arrive ahead of a gap are held here until the gap is filled, so the application never sees them out of order.

Pure state — every function returns a new struct, and nothing here performs IO. FixAlchemy.Client owns an instance per session and resets it on logon when the session negotiates ResetSeqNumFlag.

Summary

Functions

Classify an inbound message by sequence number.

Take queued messages that have become contiguous, in sequence order.

Reset both directions to 1, as a logon with ResetSeqNumFlag requires.

Jump the expected inbound sequence, per an inbound SequenceReset.

Move the outbound counter forward, as a SequenceReset-GapFill requires.

Types

inbound_outcome()

@type inbound_outcome() ::
  {:process, t()}
  | {:queued, t()}
  | {:ignore, t()}
  | {:gap, pos_integer(), pos_integer(), t()}
  | {:too_low, pos_integer(), t()}

t()

@type t() :: %FixAlchemy.Sequence{
  expected_inbound: pos_integer(),
  next_outbound: pos_integer(),
  queue: %{required(pos_integer()) => term()},
  resend_pending: boolean()
}

Functions

expected_inbound(sequence)

@spec expected_inbound(t()) :: pos_integer()

new()

@spec new() :: t()

next_outbound(sequence)

@spec next_outbound(t()) :: {pos_integer(), t()}

peek_outbound(sequence)

@spec peek_outbound(t()) :: pos_integer()

receive_inbound(sequence, seq, payload, poss_dup? \\ false)

@spec receive_inbound(t(), pos_integer(), term(), boolean()) :: inbound_outcome()

Classify an inbound message by sequence number.

payload is held verbatim when the message must wait behind a gap, so callers can pass the raw message and receive it back from release/1 once the sequence is contiguous again.

Returns one of:

  • {:process, sequence} - in order, hand it to the application
  • {:gap, begin_seq, end_seq, sequence} - send a ResendRequest for the range
  • {:queued, sequence} - held until the outstanding gap is filled
  • {:ignore, sequence} - already-seen message flagged PossDup
  • {:too_low, expected, sequence} - unrecoverable per the FIX session spec

release(sequence)

@spec release(t()) :: {[term()], t()}

Take queued messages that have become contiguous, in sequence order.

Clears the pending-resend flag once nothing is left waiting.

resend_pending?(sequence)

@spec resend_pending?(t()) :: boolean()

reset(sequence)

@spec reset(t()) :: t()

Reset both directions to 1, as a logon with ResetSeqNumFlag requires.

reset_inbound_to(sequence, new_seq)

@spec reset_inbound_to(t(), pos_integer()) :: t()

Jump the expected inbound sequence, per an inbound SequenceReset.

skip_outbound_to(sequence, seq)

@spec skip_outbound_to(t(), pos_integer()) :: t()

Move the outbound counter forward, as a SequenceReset-GapFill requires.