An Elixir library to reduce the boilerplate work one would have in order to implement a distributed cluster of strongly-consistent BEAM instances.
Pistis ensures deterministic commands through :ra
, an Erlang-based implementation of the Raft consensus protocol.
- Add the
pistis
artifact to you project's dependencies
def deps do
[
{:pistis, "~> 0.1.10"}
]
end
- Define your state machine module on
config/config.exs
config :pistis, machine: YourApp.YourStateMachine
- Add the library's entrypoint supervisor to your Elixir app's children
defmodule YourApp.Application do
# ...
def start(_type, _args) do
children = [Pistis.Core.Entrypoint]
opts = [strategy: :one_for_one, name: YourApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
- Profit
The library demands two things from its user:
- Make sure client commands are sent to Pistis' server
defmodule YourApp.SomeModule do
def your_fn_that_increments_state_by_2(_args) do
Pistis.Server.send_request({:increment_by, 2})
end
def another_fn_but_a_shy_one(_args) do
Pistis.Server.send_request(:increment)
end
end
- A concrete state-machine
defmodule YourApp.YourStateMachine do
@behaviour Pistis.Machine
def initial_state, do: 0
def process_command(%Pistis.Request{body: :increment}, current_state) do
%Pistis.Response(response: :ok, state: current_state + 1)
end
def process_command(%Pistis.Request{body: {:increment_by, num}}, current_state) do
%Pistis.Response(response: :ok, state: current_state + num)
end
end
Pistis does not inspect your message's content. It simply wraps your state-machine implementation with a Raft-aware component that takes care of delivering the request and getting a response out of it.
All requests are wrapped inside the Pistis.Request
struct. Responses should follow the same pattern by being wrapped in a Pistis.Response
struct.
Example state-machines can be found at lib/example