I’m getting some unexpected results when using fuse.
Synchronous calls behave as expected unlike async calls.
I am trying to understand what is going on.
Basically this is what I’m doing:
defmodule App.Fuse do
alias __MODULE__
@name :fuse_name
@options {{:standard, 2, 10_000}, {:reset, 60_000}}
def exec(func) do
run = fn ->
case func.() do
{:ok, result} ->
{:ok, {:ok, result}}
error ->
{:melt, error}
end
end
case :fuse.run(@name, run, :sync) do
{:ok, result} ->
{:ok, result}
:blown ->
{:error, :blown}
{:error, :not_found} ->
install_fuse()
exec(func)
end
end
def install_fuse, do:
:fuse.install(@name, @options)
def ask_status, do:
:fuse.ask(@name, :sync)
defmodule Client do
def call_synchronous do
generate_calls()
|> Enum.map(&run_in_fuse_context/1)
|> Enum.map(& &1.())
|> (fn m -> {Fuse.ask_status(), m} end).()
end
def call_async do
generate_calls()
|> Enum.shuffle
|> Enum.map(&run_in_fuse_context/1)
|> Enum.map(&Task.async/1)
|> Enum.map(&Task.await(&1, 30_0000))
|> (fn m -> {Fuse.ask_status(), m} end).()
end
defp run_in_fuse_context(c), do:
fn -> Fuse.exec(c) end
defp run_something(idx, 0), do:
{:error, idx}
defp run_something(idx, _), do:
{:ok, idx}
defp generate_calls do
# no concurrent :fuse.install
Fuse.install_fuse()
# produce :ok, :ok, :error, .., and so on
Enum.map(1..20, fn idx ->
fn -> run_something(idx, rem(idx, 3)) end
end)
end
end
end
Synchronous result:
iex> App.Fuse.Client.call_synchronous
{:blown,
[
ok: {:ok, 1},
ok: {:ok, 2},
ok: {:error, 3},
ok: {:ok, 4},
ok: {:ok, 5},
ok: {:error, 6},
ok: {:ok, 7},
ok: {:ok, 8},
ok: {:error, 9},
error: :blown,
error: :blown,
error: :blown,
error: :blown,
error: :blown,
error: :blown,
error: :blown,
error: :blown,
error: :blown,
error: :blown,
error: :blown
]}
Async result:
iex> App.Fuse.Client.call_async
{:blown,
[
ok: {:ok, 17},
ok: {:ok, 11},
ok: {:ok, 10},
ok: {:error, 18},
ok: {:ok, 20},
ok: {:ok, 1},
ok: {:error, 9},
ok: {:error, 3},
ok: {:ok, 14},
ok: {:ok, 4},
ok: {:ok, 7},
ok: {:ok, 2},
ok: {:error, 15},
ok: {:error, 6},
ok: {:ok, 13},
ok: {:ok, 8},
ok: {:ok, 5},
ok: {:error, 12},
ok: {:ok, 16},
ok: {:ok, 19}
]}
So after all of the async calls are done, the fuse is blown.
Any ideas on why the circuit doesn’t break “somewhere in the middle” ?