Visor

Visor adds VSR (View Stamped Replication) on top of GenServer.

If you want easy process supervision, add a Supervisor.

If you want easy replication, add Visor!

Usage

Create a module, use Visor, and implement the callbacks. Let's create a replicated cache.

Define a module called Cache, set the initial_state to an empty map (%{}) and implement the handle_commit callback which returns nil and merges the passed in data with the existing state.

defmodule Cache do
  use Visor

  @impl Visor
  def initial_state(_opts), do: %{}

  @impl Visor
  def handle_commit({:add, item}, state) do
    {:ok, nil, Map.merge(state, item)}
  end
end

Now, add Cache to your application's supervision tree, and configure your cluster_size (how many nodes you want this to replicate across)


  def start(_type, _args) do
    children = [
      {Cache, cluster_size: 3}
    ]

    opts = [strategy: :one_for_one, name: Cache.Supervisor]
    Supervisor.start_link(children, opts)
  end

And that's it. You now have a cache that's replicated across 3 nodes. It handles replication, leader election, and recovery.

To add an item to your cache, commit the data:

Cache.commit({:add, %{user1: %{email: "advisor1@example.com"}}})
#=> nil

Cache.commit({:add, %{user2: %{email: "advisor2@example.com"}}})
#=> nil

Get the full state of your cache with:

Cache.get()

#=> %{email: "advisor@example.com"}
%{
  user1: %{email: "advisor1@example.com"},
  user2: %{email: "advisor1@example.com"}
}

Pass in an accessor list to get_in your cache for quicker access:

Cache.get_in([:user1, :email])

#=> "advisor1@example.com"