Current section
Files
Jump to
Current section
Files
lib/paxos_kv.ex
defmodule PaxosKV do
@moduledoc """
`PaxosKV` is the main API module. Most of the time you should interact with
the application via functions in this module.
The most important function you need to know is `PaxosKV.put`.
"""
alias PaxosKV.{Helpers, Proposer, Learner}
@doc """
Stores the given value under the key in the collective memory (a KV store) of
the cluster.
PaxosKV.put(key, value)
It can take further options
* `bucket: b` -- use bucket `b` to store the key-value pair
* `pid: p` -- keep the key-value pair as long as pid `p` is alive
* `node: n` -- keep the key-value pair as long as node `n` is connected
* `no_quorum: :retry | :fail | :return` -- Try again, crash or just return an
error tuple when there's no quorum.
The return value of the call is `{:ok, value}` when there's a consensus value
for the `key`. The `value` returned is not always the `value` argument of the
`PaxosKV.put` function, it can be a value proposed by another process. It can
also return `{:error, reason}`, when something goes wrong.
`{:error, :no_quorum}` means the cluster does not have enough nodes. In order to
make a decision / reach consensus, more than `cluster_size / 2` nodes have to
be part of the cluster. (Cluster size is a config param.) The option `:no_quorum`
can change the behavior of `put`. Whern set to `:retry`, it won't return this
tuple, but it will try again and again until it can reach enough nodes. The
option `no_quorum: :fail` will generate an exception instead.
"""
def put(key, value, opts \\ []) do
case Proposer.propose(key, value, opts) do
{:ok, {value, _meta}} ->
{:ok, value}
{:error, :invalid_value} = error ->
error
{:error, :no_quorum} = error ->
case Keyword.get(opts, :no_quorum, :return) do
:retry ->
Helpers.random_backoff()
put(key, value, opts)
:fail ->
throw(error)
:return ->
error
end
end
end
@doc """
Returns the value for the given key in the cluster.
It renurns `nil` when the key is not registered. The option `default: d`
defines the value `d` that should be returned instead of nil when the key
is not yet set or has been deleted.
"""
def get(key, opts \\ []) do
case Learner.get(key, opts) do
{value, _meta} -> value
_ -> Keyword.get(opts, :default)
end
end
@doc """
Returns the pid the key-vaue pair is bound to. This is the pid set by the
`pid: p` option in `put/3`. `default: d` can be used the return `d` instead
of `nil` when there's not pid set for the value or the key-value pair is not
registered.
"""
def pid(key, opts \\ []) do
case Learner.get(key, opts) do
{_value, %{pid: pid}} -> pid
_ -> Keyword.get(opts, :default)
end
end
@doc """
Returns the node name the key-vaue pair is bound to. This is the node set by
the `node: n` option in `put/3`. It returns `nil` when there is no node is
bound to the key-vaue pair, or the key-value pair is not registered. The
option `default: d` makes the call return d when it would return `nil`
otherwise.
"""
def node(key, opts \\ []) do
case Learner.get(key, opts) do
{_value, %{node: node}} -> node
_ -> Keyword.get(opts, :default)
end
end
end