Packages
finitomata
0.18.1
0.41.0
0.40.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.1
0.30.3
0.30.2
0.30.1
0.30.0
0.29.10
0.29.9
0.29.8
0.29.7
0.29.6
0.29.5
0.29.4
0.29.3
0.29.2
0.29.1
0.29.0
0.28.1
0.28.0
0.27.1
0.27.0
0.26.4
0.26.3
0.26.2
0.26.1
0.26.0
0.25.0
0.24.4
0.24.3
0.24.2
0.24.1
0.24.0
0.23.7
0.23.6
0.23.5
0.23.4
0.23.3
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.2
0.20.1
0.20.0
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.1
0.17.0
0.16.0
0.15.1
0.15.0
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.13.0
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.1
0.1.0
The FSM implementation generated from PlantUML textual representation.
Current section
Files
Jump to
Current section
Files
lib/infinitomata.ex
defmodule Infinitomata do
@moduledoc """
The sibling of `Finitomata`, but runs transparently in the cluster.
If you want to use a _stateful consistent hash ring_ like [`libring`](https://hexdocs.pm/libring),
implement the behaviour `Finitomata.ClusterInfo` wrapping calls to it and
invoke `Finitomata.ClusterInfo.init(Impl)` before using `Infinitomata.start_fsm/4`.
The example of such an implementation for `libring` (assuming the named ring `@ring`
has been started in the supervision tree) follows.
```elixir
defmodule MyApp.ClusterInfo do
@moduledoc false
@behaviour Finitomata.ClusterInfo
@impl Finitomata.ClusterInfo
def nodes, do: HashRing.nodes(@ring)
@impl Finitomata.ClusterInfo
def whois(id), do: HashRing.key_to_node(@ring, id)
end
```
"""
@moduledoc since: "0.15.0"
alias Finitomata.{ClusterInfo, State, Transition}
alias Finitomata.Distributed.GroupMonitor, as: InfMon
alias Finitomata.Distributed.Supervisor, as: InfSup
alias Finitomata.Supervisor, as: FinSup
@doc since: "0.16.0"
def start_link(id \\ nil) do
id
|> FinSup.infinitomata_name()
|> InfSup.start_link()
end
@doc since: "0.16.0"
def child_spec(id \\ nil) do
id = FinSup.infinitomata_name(id)
Supervisor.child_spec({InfSup, id}, id: {InfSup, id})
end
defp distributed_call(fun, id, target, args) do
case InfSup.get(id, target) do
%{node: node} ->
:rpc.call(node, Finitomata, fun, [id, target | List.wrap(args)])
_ ->
{:error, :not_started}
end
end
@doc since: "0.16.0"
@doc "Count of children"
def count(id \\ nil) do
id
|> FinSup.infinitomata_name()
|> InfMon.count()
end
@doc since: "0.16.0"
@doc "The full state with all te children, might be a heavy map"
def all(id \\ nil) do
id
|> FinSup.infinitomata_name()
|> InfSup.all()
end
@doc since: "0.18.0"
@doc "Returns the random _FSM_ from the pool"
def random(id \\ nil) do
id
|> FinSup.infinitomata_name()
|> InfSup.all()
|> Map.keys()
|> Enum.random()
end
@doc """
Starts the FSM somewhere in the cluster.
See `Finitomata.start_fsm/4`.
"""
@doc since: "0.15.0"
@spec start_fsm(Finitomata.id(), Finitomata.fsm_name(), module(), any()) ::
DynamicSupervisor.on_start_child()
def start_fsm(id \\ nil, target, implementation, payload) do
id = FinSup.infinitomata_name(id)
case InfSup.get(id, target) do
nil ->
node = ClusterInfo.whois({id, target})
case :rpc.block_call(node, Finitomata, :start_fsm, [id, target, implementation, payload]) do
{:ok, pid} ->
# local_pid = :rpc.call(node, :erlang, :list_to_pid, [:erlang.pid_to_list(pid)]).
:ok = :rpc.block_call(node, :pg, :join, [InfSup.group(id), pid])
{:ok, pid}
{:badrpc, reason} ->
{:error, reason}
end
%{node: node, pid: pid} ->
{:error, {:already_started, {node, pid}}}
end
end
@doc """
Initiates the transition in the cluster.
See `Finitomata.transition/4`.
"""
@doc since: "0.15.0"
@spec transition(
Finitomata.id(),
Finitomata.fsm_name(),
Transition.event() | {Transition.event(), State.payload()},
non_neg_integer()
) ::
:ok
def transition(id \\ nil, target, event_payload, delay \\ 0) do
id = FinSup.infinitomata_name(id)
distributed_call(:transition, id, target, [event_payload, delay])
end
@doc """
The state of the FSM in the cluster.
See `Finitomata.state/3`.
"""
@doc since: "0.15.0"
def state(id \\ nil, target, reload? \\ :full) do
id = FinSup.infinitomata_name(id)
distributed_call(:state, id, target, reload?)
end
end