Packages
tai
0.0.68
0.0.75
0.0.74
0.0.73
0.0.72
0.0.71
0.0.70
0.0.69
0.0.68
0.0.67
0.0.66
0.0.65
0.0.64
0.0.63
0.0.62
0.0.61
0.0.60
0.0.59
0.0.58
0.0.57
0.0.56
0.0.55
0.0.54
0.0.53
0.0.52
0.0.51
0.0.50
0.0.49
0.0.48
0.0.47
0.0.46
0.0.45
0.0.44
0.0.43
0.0.42
0.0.41
0.0.40
0.0.39
0.0.38
0.0.37
0.0.36
0.0.35
0.0.34
0.0.33
0.0.32
0.0.31
0.0.30
0.0.29
0.0.28
0.0.27
0.0.26
0.0.25
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
A composable, real time, market data and trade execution toolkit
Current section
Files
Jump to
Current section
Files
lib/tai/orders/order_transition_worker.ex
defmodule Tai.Orders.OrderTransitionWorker do
use GenServer
alias Tai.Orders.{Order, Services}
@moduledoc """
The OrderTransitionWorker acts as a locking mechanism to ensure an order
can only be updated sequentially to avoid race conditions when receiving
the accepted response from a request and asynchronously receiving the result
of that request.
Below is an example of the sequence that can occur when creating an order:
send HTTP request to create order ->
<- asynchronously receive on stream that order created successfully
<- response from HTTP request that create order was accepted
Multiple orders can be updated in parallel by using 2 or more workers. Orders
are deterministically sent to the same worker using the following algorithm.
```
hash(order.client_id) % order_transition_worker_count
```
"""
@type order :: Order.t()
@type client_id :: Order.client_id()
@type attrs :: Services.ApplyOrderTransition.attrs()
@spec start_link(pos_integer) :: GenServer.on_start()
def start_link(idx) do
name = process_name(idx)
GenServer.start_link(__MODULE__, idx, name: name)
end
@spec process_name(pos_integer) :: atom
def process_name(idx), do: :"#{__MODULE__}_#{idx}"
@spec apply(client_id, attrs) :: {:ok, order} | {:error, term}
def apply(client_id, transition_attrs) do
client_id
|> worker_idx()
|> process_name()
|> GenServer.call({:apply, client_id, transition_attrs})
end
@impl true
def init(state) do
{:ok, state}
end
@impl true
def handle_call({:apply, client_id, transition_attrs}, _from, state) do
result = Services.ApplyOrderTransition.call(client_id, transition_attrs)
{:reply, result, state}
end
defp worker_idx(client_id) do
config = Tai.Config.parse()
hash = Murmur.hash_x86_32(client_id)
rem(hash, config.order_transition_workers)
end
end