Have a separate subtree for each fetcher with a supervisor supervising the fetcher and a Task.Supervisor that is only used by the fetcher, so that looking at the Task.Supervisor in that subtree will show which fetcher is causing any memory, message, or blocking problems.pull/707/head
parent
9b3ed74507
commit
09051ceb8a
@ -0,0 +1,110 @@ |
||||
defmodule Indexer.BlockFetcher.Catchup.BoundIntervalSupervisor do |
||||
@moduledoc """ |
||||
Supervises the `Indexer.BlockerFetcher.Catchup` with exponential backoff for restarts. |
||||
""" |
||||
|
||||
# NOT a `Supervisor` because of the `Task` restart strategies are custom. |
||||
use GenServer |
||||
|
||||
require Logger |
||||
|
||||
alias Indexer.{BlockFetcher, BoundInterval} |
||||
alias Indexer.BlockFetcher.Catchup |
||||
|
||||
# milliseconds |
||||
@block_interval 5_000 |
||||
|
||||
@enforce_keys ~w(bound_interval catchup)a |
||||
defstruct bound_interval: nil, |
||||
catchup: %Catchup{}, |
||||
task: nil |
||||
|
||||
def child_spec(arg) do |
||||
# The `child_spec` from `use Supervisor` because the one from `use GenServer` will set the `type` to `:worker` |
||||
# instead of `:supervisor` and use the wrong shutdown timeout |
||||
Supervisor.child_spec(%{id: __MODULE__, start: {__MODULE__, :start_link, [arg]}, type: :supervisor}, []) |
||||
end |
||||
|
||||
@doc """ |
||||
Starts supervisor of `Indexer.BlockerFetcher.Catchup` and `Indexer.BlockFetcher.Realtime`. |
||||
|
||||
For `named_arguments` see `Indexer.BlockFetcher.new/1`. For `t:GenServer.options/0` see `GenServer.start_link/3`. |
||||
""" |
||||
@spec start_link([named_arguments :: list() | GenServer.options()]) :: {:ok, pid} |
||||
def start_link([named_arguments, gen_server_options]) when is_map(named_arguments) and is_list(gen_server_options) do |
||||
GenServer.start_link(__MODULE__, named_arguments, gen_server_options) |
||||
end |
||||
|
||||
@impl GenServer |
||||
def init(named_arguments) do |
||||
state = new(named_arguments) |
||||
|
||||
send(self(), :catchup_index) |
||||
|
||||
{:ok, state} |
||||
end |
||||
|
||||
defp new(%{block_fetcher: common_block_fetcher} = named_arguments) do |
||||
block_fetcher = %BlockFetcher{common_block_fetcher | broadcast: false, callback_module: Catchup} |
||||
|
||||
block_interval = Map.get(named_arguments, :block_interval, @block_interval) |
||||
minimum_interval = div(block_interval, 2) |
||||
bound_interval = BoundInterval.within(minimum_interval..(minimum_interval * 10)) |
||||
|
||||
%__MODULE__{ |
||||
catchup: %Catchup{block_fetcher: block_fetcher}, |
||||
bound_interval: bound_interval |
||||
} |
||||
end |
||||
|
||||
@impl GenServer |
||||
def handle_info(:catchup_index, %__MODULE__{catchup: %Catchup{} = catchup} = state) do |
||||
{:noreply, |
||||
%__MODULE__{state | task: Task.Supervisor.async_nolink(Catchup.TaskSupervisor, Catchup, :task, [catchup])}} |
||||
end |
||||
|
||||
def handle_info( |
||||
{ref, %{first_block_number: first_block_number, missing_block_count: missing_block_count}}, |
||||
%__MODULE__{ |
||||
bound_interval: bound_interval, |
||||
task: %Task{ref: ref} |
||||
} = state |
||||
) |
||||
when is_integer(missing_block_count) do |
||||
new_bound_interval = |
||||
case missing_block_count do |
||||
0 -> |
||||
Logger.info("Index already caught up in #{first_block_number}-0") |
||||
|
||||
BoundInterval.increase(bound_interval) |
||||
|
||||
_ -> |
||||
Logger.info("Index had to catch up #{missing_block_count} blocks in #{first_block_number}-0") |
||||
|
||||
BoundInterval.decrease(bound_interval) |
||||
end |
||||
|
||||
Process.demonitor(ref, [:flush]) |
||||
|
||||
interval = new_bound_interval.current |
||||
|
||||
Logger.info(fn -> |
||||
"Checking if index needs to catch up in #{interval}ms" |
||||
end) |
||||
|
||||
Process.send_after(self(), :catchup_index, interval) |
||||
|
||||
{:noreply, %__MODULE__{state | bound_interval: new_bound_interval, task: nil}} |
||||
end |
||||
|
||||
def handle_info( |
||||
{:DOWN, ref, :process, pid, reason}, |
||||
%__MODULE__{task: %Task{pid: pid, ref: ref}} = state |
||||
) do |
||||
Logger.error(fn -> "Catchup index stream exited with reason (#{inspect(reason)}). Restarting" end) |
||||
|
||||
send(self(), :catchup_index) |
||||
|
||||
{:noreply, %__MODULE__{state | task: nil}} |
||||
end |
||||
end |
@ -1,110 +1,38 @@ |
||||
defmodule Indexer.BlockFetcher.Catchup.Supervisor do |
||||
@moduledoc """ |
||||
Supervises the `Indexer.BlockerFetcher.Catchup` with exponential backoff for restarts. |
||||
Supervises `Indexer.BlockFetcher.Catchup.TaskSupervisor` and `Indexer.BlockFetcher.Catchup.BoundIntervalSupervisor` |
||||
""" |
||||
|
||||
# NOT a `Supervisor` because of the `Task` restart strategies are custom. |
||||
use GenServer |
||||
use Supervisor |
||||
|
||||
require Logger |
||||
alias Indexer.BlockFetcher.Catchup.BoundIntervalSupervisor |
||||
|
||||
alias Indexer.{BlockFetcher, BoundInterval} |
||||
alias Indexer.BlockFetcher.Catchup |
||||
|
||||
# milliseconds |
||||
@block_interval 5_000 |
||||
|
||||
@enforce_keys ~w(bound_interval catchup)a |
||||
defstruct bound_interval: nil, |
||||
catchup: %Catchup{}, |
||||
task: nil |
||||
|
||||
def child_spec(arg) do |
||||
# The `child_spec` from `use Supervisor` because the one from `use GenServer` will set the `type` to `:worker` |
||||
# instead of `:supervisor` and use the wrong shutdown timeout |
||||
Supervisor.child_spec(%{id: __MODULE__, start: {__MODULE__, :start_link, [arg]}, type: :supervisor}, []) |
||||
end |
||||
|
||||
@doc """ |
||||
Starts supervisor of `Indexer.BlockerFetcher.Catchup` and `Indexer.BlockFetcher.Realtime`. |
||||
|
||||
For `named_arguments` see `Indexer.BlockFetcher.new/1`. For `t:GenServer.options/0` see `GenServer.start_link/3`. |
||||
""" |
||||
@spec start_link([named_arguments :: list() | GenServer.options()]) :: {:ok, pid} |
||||
def start_link([named_arguments, gen_server_options]) when is_map(named_arguments) and is_list(gen_server_options) do |
||||
GenServer.start_link(__MODULE__, named_arguments, gen_server_options) |
||||
end |
||||
|
||||
@impl GenServer |
||||
def init(named_arguments) do |
||||
state = new(named_arguments) |
||||
|
||||
send(self(), :catchup_index) |
||||
|
||||
{:ok, state} |
||||
def child_spec([init_arguments]) do |
||||
child_spec([init_arguments, []]) |
||||
end |
||||
|
||||
defp new(%{block_fetcher: common_block_fetcher} = named_arguments) do |
||||
block_fetcher = %BlockFetcher{common_block_fetcher | broadcast: false, callback_module: Catchup} |
||||
|
||||
block_interval = Map.get(named_arguments, :block_interval, @block_interval) |
||||
minimum_interval = div(block_interval, 2) |
||||
bound_interval = BoundInterval.within(minimum_interval..(minimum_interval * 10)) |
||||
|
||||
%__MODULE__{ |
||||
catchup: %Catchup{block_fetcher: block_fetcher}, |
||||
bound_interval: bound_interval |
||||
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do |
||||
default = %{ |
||||
id: __MODULE__, |
||||
start: {__MODULE__, :start_link, start_link_arguments}, |
||||
type: :supervisor |
||||
} |
||||
end |
||||
|
||||
@impl GenServer |
||||
def handle_info(:catchup_index, %__MODULE__{catchup: %Catchup{} = catchup} = state) do |
||||
{:noreply, |
||||
%__MODULE__{state | task: Task.Supervisor.async_nolink(Indexer.TaskSupervisor, Catchup, :task, [catchup])}} |
||||
Supervisor.child_spec(default, []) |
||||
end |
||||
|
||||
def handle_info( |
||||
{ref, %{first_block_number: first_block_number, missing_block_count: missing_block_count}}, |
||||
%__MODULE__{ |
||||
bound_interval: bound_interval, |
||||
task: %Task{ref: ref} |
||||
} = state |
||||
) |
||||
when is_integer(missing_block_count) do |
||||
new_bound_interval = |
||||
case missing_block_count do |
||||
0 -> |
||||
Logger.info("Index already caught up in #{first_block_number}-0") |
||||
|
||||
BoundInterval.increase(bound_interval) |
||||
|
||||
_ -> |
||||
Logger.info("Index had to catch up #{missing_block_count} blocks in #{first_block_number}-0") |
||||
|
||||
BoundInterval.decrease(bound_interval) |
||||
end |
||||
|
||||
Process.demonitor(ref, [:flush]) |
||||
|
||||
interval = new_bound_interval.current |
||||
|
||||
Logger.info(fn -> |
||||
"Checking if index needs to catch up in #{interval}ms" |
||||
end) |
||||
|
||||
Process.send_after(self(), :catchup_index, interval) |
||||
|
||||
{:noreply, %__MODULE__{state | bound_interval: new_bound_interval, task: nil}} |
||||
def start_link(arguments, gen_server_options \\ []) do |
||||
Supervisor.start_link(__MODULE__, arguments, gen_server_options) |
||||
end |
||||
|
||||
def handle_info( |
||||
{:DOWN, ref, :process, pid, reason}, |
||||
%__MODULE__{task: %Task{pid: pid, ref: ref}} = state |
||||
) do |
||||
Logger.error(fn -> "Catchup index stream exited with reason (#{inspect(reason)}). Restarting" end) |
||||
|
||||
send(self(), :catchup_index) |
||||
|
||||
{:noreply, %__MODULE__{state | task: nil}} |
||||
@impl Supervisor |
||||
def init(bound_interval_supervisor_arguments) do |
||||
Supervisor.init( |
||||
[ |
||||
{Task.Supervisor, name: Indexer.BlockFetcher.Catchup.TaskSupervisor}, |
||||
{BoundIntervalSupervisor, [bound_interval_supervisor_arguments, [name: BoundIntervalSupervisor]]} |
||||
], |
||||
strategy: :one_for_one |
||||
) |
||||
end |
||||
end |
||||
|
@ -0,0 +1,38 @@ |
||||
defmodule Indexer.CoinBalance.Supervisor do |
||||
@moduledoc """ |
||||
Supervises `Indexer.CoinBalance.Fetcher` and its batch tasks through `Indexer.CoinBalance.TaskSupervisor` |
||||
""" |
||||
|
||||
use Supervisor |
||||
|
||||
alias Indexer.CoinBalance.Fetcher |
||||
|
||||
def child_spec([init_arguments]) do |
||||
child_spec([init_arguments, []]) |
||||
end |
||||
|
||||
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do |
||||
default = %{ |
||||
id: __MODULE__, |
||||
start: {__MODULE__, :start_link, start_link_arguments}, |
||||
type: :supervisor |
||||
} |
||||
|
||||
Supervisor.child_spec(default, []) |
||||
end |
||||
|
||||
def start_link(arguments, gen_server_options \\ []) do |
||||
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__)) |
||||
end |
||||
|
||||
@impl Supervisor |
||||
def init(fetcher_arguments) do |
||||
Supervisor.init( |
||||
[ |
||||
{Task.Supervisor, name: Indexer.CoinBalance.TaskSupervisor}, |
||||
{Fetcher, [fetcher_arguments, [name: Fetcher]]} |
||||
], |
||||
strategy: :one_for_one |
||||
) |
||||
end |
||||
end |
@ -0,0 +1,39 @@ |
||||
defmodule Indexer.InternalTransaction.Supervisor do |
||||
@moduledoc """ |
||||
Supervises `Indexer.InternalTransaction.Fetcher` and its batch tasks through |
||||
`Indexer.InternalTransaction.TaskSupervisor`. |
||||
""" |
||||
|
||||
use Supervisor |
||||
|
||||
alias Indexer.InternalTransaction.Fetcher |
||||
|
||||
def child_spec([init_arguments]) do |
||||
child_spec([init_arguments, []]) |
||||
end |
||||
|
||||
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do |
||||
default = %{ |
||||
id: __MODULE__, |
||||
start: {__MODULE__, :start_link, start_link_arguments}, |
||||
type: :supervisor |
||||
} |
||||
|
||||
Supervisor.child_spec(default, []) |
||||
end |
||||
|
||||
def start_link(arguments, gen_server_options \\ []) do |
||||
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__)) |
||||
end |
||||
|
||||
@impl Supervisor |
||||
def init(fetcher_arguments) do |
||||
Supervisor.init( |
||||
[ |
||||
{Task.Supervisor, name: Indexer.InternalTransaction.TaskSupervisor}, |
||||
{Fetcher, [fetcher_arguments, [name: Fetcher]]} |
||||
], |
||||
strategy: :one_for_one |
||||
) |
||||
end |
||||
end |
@ -0,0 +1,39 @@ |
||||
defmodule Indexer.PendingTransaction.Supervisor do |
||||
@moduledoc """ |
||||
Supervises `Indexer.PendingTransaction.Fetcher` and its batch tasks through |
||||
`Indexer.PendingTransaction.TaskSupervisor`. |
||||
""" |
||||
|
||||
use Supervisor |
||||
|
||||
alias Indexer.PendingTransaction.Fetcher |
||||
|
||||
def child_spec([init_arguments]) do |
||||
child_spec([init_arguments, []]) |
||||
end |
||||
|
||||
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do |
||||
default = %{ |
||||
id: __MODULE__, |
||||
start: {__MODULE__, :start_link, start_link_arguments}, |
||||
type: :supervisor |
||||
} |
||||
|
||||
Supervisor.child_spec(default, []) |
||||
end |
||||
|
||||
def start_link(arguments, gen_server_options \\ []) do |
||||
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__)) |
||||
end |
||||
|
||||
@impl Supervisor |
||||
def init(fetcher_arguments) do |
||||
Supervisor.init( |
||||
[ |
||||
{Task.Supervisor, name: Indexer.PendingTransaction.TaskSupervisor}, |
||||
{Fetcher, [fetcher_arguments, [name: Fetcher]]} |
||||
], |
||||
strategy: :one_for_one |
||||
) |
||||
end |
||||
end |
@ -0,0 +1,38 @@ |
||||
defmodule Indexer.Token.Supervisor do |
||||
@moduledoc """ |
||||
Supervises `Indexer.Token.Fetcher` and its batch tasks through `Indexer.Token.TaskSupervisor`. |
||||
""" |
||||
|
||||
use Supervisor |
||||
|
||||
alias Indexer.Token.Fetcher |
||||
|
||||
def child_spec([init_arguments]) do |
||||
child_spec([init_arguments, []]) |
||||
end |
||||
|
||||
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do |
||||
default = %{ |
||||
id: __MODULE__, |
||||
start: {__MODULE__, :start_link, start_link_arguments}, |
||||
type: :supervisor |
||||
} |
||||
|
||||
Supervisor.child_spec(default, []) |
||||
end |
||||
|
||||
def start_link(arguments, gen_server_options \\ []) do |
||||
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__)) |
||||
end |
||||
|
||||
@impl Supervisor |
||||
def init(fetcher_arguments) do |
||||
Supervisor.init( |
||||
[ |
||||
{Task.Supervisor, name: Indexer.Token.TaskSupervisor}, |
||||
{Fetcher, [fetcher_arguments, [name: Fetcher]]} |
||||
], |
||||
strategy: :one_for_one |
||||
) |
||||
end |
||||
end |
@ -0,0 +1,38 @@ |
||||
defmodule Indexer.TokenBalance.Supervisor do |
||||
@moduledoc """ |
||||
Supervises `Indexer.TokenBalance.Fetcher` and its batch tasks through `Indexer.TokenBalance.TaskSupervisor` |
||||
""" |
||||
|
||||
use Supervisor |
||||
|
||||
alias Indexer.TokenBalance.Fetcher |
||||
|
||||
def child_spec([init_arguments]) do |
||||
child_spec([init_arguments, []]) |
||||
end |
||||
|
||||
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do |
||||
default = %{ |
||||
id: __MODULE__, |
||||
start: {__MODULE__, :start_link, start_link_arguments}, |
||||
type: :supervisor |
||||
} |
||||
|
||||
Supervisor.child_spec(default, []) |
||||
end |
||||
|
||||
def start_link(arguments, gen_server_options \\ []) do |
||||
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__)) |
||||
end |
||||
|
||||
@impl Supervisor |
||||
def init(fetcher_arguments) do |
||||
Supervisor.init( |
||||
[ |
||||
{Task.Supervisor, name: Indexer.TokenBalance.TaskSupervisor}, |
||||
{Fetcher, [fetcher_arguments, [name: Fetcher]]} |
||||
], |
||||
strategy: :one_for_one |
||||
) |
||||
end |
||||
end |
@ -0,0 +1,9 @@ |
||||
defmodule Indexer.BlockFetcher.Catchup.Supervisor.Case do |
||||
alias Indexer.BlockFetcher.Catchup |
||||
|
||||
def start_supervised!(fetcher_arguments) when is_map(fetcher_arguments) do |
||||
[fetcher_arguments] |
||||
|> Catchup.Supervisor.child_spec() |
||||
|> ExUnit.Callbacks.start_supervised!() |
||||
end |
||||
end |
@ -0,0 +1,18 @@ |
||||
defmodule Indexer.CoinBalance.Supervisor.Case do |
||||
alias Indexer.CoinBalance |
||||
|
||||
def start_supervised!(fetcher_arguments \\ []) when is_list(fetcher_arguments) do |
||||
merged_fetcher_arguments = |
||||
Keyword.merge( |
||||
fetcher_arguments, |
||||
flush_interval: 50, |
||||
init_chunk_size: 1, |
||||
max_batch_size: 1, |
||||
max_concurrency: 1 |
||||
) |
||||
|
||||
[merged_fetcher_arguments] |
||||
|> CoinBalance.Supervisor.child_spec() |
||||
|> ExUnit.Callbacks.start_supervised!() |
||||
end |
||||
end |
@ -1,16 +0,0 @@ |
||||
defmodule Indexer.CoinBalanceFetcherCase do |
||||
alias Indexer.CoinBalanceFetcher |
||||
|
||||
def start_supervised!(options \\ []) when is_list(options) do |
||||
options |
||||
|> Keyword.merge( |
||||
flush_interval: 50, |
||||
init_chunk_size: 1, |
||||
max_batch_size: 1, |
||||
max_concurrency: 1, |
||||
name: CoinBalanceFetcher |
||||
) |
||||
|> CoinBalanceFetcher.child_spec() |
||||
|> ExUnit.Callbacks.start_supervised!() |
||||
end |
||||
end |
@ -0,0 +1,18 @@ |
||||
defmodule Indexer.InternalTransaction.Supervisor.Case do |
||||
alias Indexer.InternalTransaction |
||||
|
||||
def start_supervised!(fetcher_arguments \\ []) when is_list(fetcher_arguments) do |
||||
merged_fetcher_arguments = |
||||
Keyword.merge( |
||||
fetcher_arguments, |
||||
flush_interval: 50, |
||||
init_chunk_size: 1, |
||||
max_batch_size: 1, |
||||
max_concurrency: 1 |
||||
) |
||||
|
||||
[merged_fetcher_arguments] |
||||
|> InternalTransaction.Supervisor.child_spec() |
||||
|> ExUnit.Callbacks.start_supervised!() |
||||
end |
||||
end |
@ -1,10 +0,0 @@ |
||||
defmodule Indexer.InternalTransactionFetcherCase do |
||||
alias Indexer.InternalTransactionFetcher |
||||
|
||||
def start_supervised!(options \\ []) when is_list(options) do |
||||
options |
||||
|> Keyword.put(:name, InternalTransactionFetcher) |
||||
|> InternalTransactionFetcher.child_spec() |
||||
|> ExUnit.Callbacks.start_supervised!() |
||||
end |
||||
end |
@ -0,0 +1,18 @@ |
||||
defmodule Indexer.PendingTransaction.Supervisor.Case do |
||||
alias Indexer.PendingTransaction |
||||
|
||||
def start_supervised!(fetcher_arguments \\ []) when is_list(fetcher_arguments) do |
||||
merged_fetcher_arguments = |
||||
Keyword.merge( |
||||
fetcher_arguments, |
||||
flush_interval: 50, |
||||
init_chunk_size: 1, |
||||
max_batch_size: 1, |
||||
max_concurrency: 1 |
||||
) |
||||
|
||||
[merged_fetcher_arguments] |
||||
|> PendingTransaction.Supervisor.child_spec() |
||||
|> ExUnit.Callbacks.start_supervised!() |
||||
end |
||||
end |
@ -0,0 +1,18 @@ |
||||
defmodule Indexer.Token.Supervisor.Case do |
||||
alias Indexer.Token |
||||
|
||||
def start_supervised!(fetcher_arguments \\ []) when is_list(fetcher_arguments) do |
||||
merged_fetcher_arguments = |
||||
Keyword.merge( |
||||
fetcher_arguments, |
||||
flush_interval: 50, |
||||
init_chunk_size: 1, |
||||
max_batch_size: 1, |
||||
max_concurrency: 1 |
||||
) |
||||
|
||||
[merged_fetcher_arguments] |
||||
|> Token.Supervisor.child_spec() |
||||
|> ExUnit.Callbacks.start_supervised!() |
||||
end |
||||
end |
@ -0,0 +1,18 @@ |
||||
defmodule Indexer.TokenBalance.Supervisor.Case do |
||||
alias Indexer.TokenBalance |
||||
|
||||
def start_supervised!(fetcher_arguments \\ []) when is_list(fetcher_arguments) do |
||||
merged_fetcher_arguments = |
||||
Keyword.merge( |
||||
fetcher_arguments, |
||||
flush_interval: 50, |
||||
init_chunk_size: 1, |
||||
max_batch_size: 1, |
||||
max_concurrency: 1 |
||||
) |
||||
|
||||
[merged_fetcher_arguments] |
||||
|> TokenBalance.Supervisor.child_spec() |
||||
|> ExUnit.Callbacks.start_supervised!() |
||||
end |
||||
end |
@ -1,16 +0,0 @@ |
||||
defmodule Indexer.TokenBalanceFetcherCase do |
||||
alias Indexer.TokenBalanceFetcher |
||||
|
||||
def start_supervised!(options \\ []) when is_list(options) do |
||||
options |
||||
|> Keyword.merge( |
||||
flush_interval: 50, |
||||
init_chunk_size: 1, |
||||
max_batch_size: 1, |
||||
max_concurrency: 1, |
||||
name: TokenBalanceFetcher |
||||
) |
||||
|> TokenBalanceFetcher.child_spec() |
||||
|> ExUnit.Callbacks.start_supervised!() |
||||
end |
||||
end |
@ -1,16 +0,0 @@ |
||||
defmodule Indexer.TokenFetcherCase do |
||||
alias Indexer.TokenFetcher |
||||
|
||||
def start_supervised!(options \\ []) when is_list(options) do |
||||
options |
||||
|> Keyword.merge( |
||||
flush_interval: 50, |
||||
init_chunk_size: 1, |
||||
max_batch_size: 1, |
||||
max_concurrency: 1, |
||||
name: TokenFetcher |
||||
) |
||||
|> TokenFetcher.child_spec() |
||||
|> ExUnit.Callbacks.start_supervised!() |
||||
end |
||||
end |
Loading…
Reference in new issue