# `PaxosKV`

`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`.

# `get`

Returns the value for the given key in the cluster.

The function returns:
- `{:ok, value}` when the key is registered
- `{:ok, default}` when the key is not found and the `default: d` option is provided
- `{:error, :not_found}` when the key is not registered and no default is provided
- `{:error, :no_quorum}` when the cluster doesn't have enough nodes to reach consensus

The option `default: d` defines the value `d` that should be returned (as `{:ok, d}`)
when the key is not yet set or has been deleted.

## Examples

    iex> PaxosKV.put(:user_name, "Alice")
    iex> PaxosKV.get(:user_name)
    {:ok, "Alice"}

    iex> PaxosKV.get(:non_existent_key)
    {:error, :not_found}

    iex> PaxosKV.get(:non_existent_key, default: "default_value")
    {:ok, "default_value"}

# `keys`

Returns a list of all keys stored in the cluster.

This function queries all nodes in the cluster and returns a deduplicated
list of all keys that have been stored. You can optionally specify a bucket
to query keys from a specific namespace.

The function returns:
- A list of keys (any Erlang terms) when successful
- `[]` (empty list) when there are no keys or when the cluster is not available

## Examples

    iex> PaxosKV.put(:user_name, "Alice")
    iex> PaxosKV.put(:counter, 42)
    iex> PaxosKV.keys()
    [:user_name, :counter]

    # Query a specific bucket
    iex> PaxosKV.keys(MyBucket)
    [:key1, :key2]

# `node`

Returns the node name the key-value pair is bound to. This is the node set by
the `node: n` option in `put/3`.

The function returns:
- `{:ok, node}` when a node is associated with the key
- `{:ok, default}` when the key is not found and the `default: d` option is provided
- `{:error, :not_found}` when the key is not registered and no default is provided
- `{:error, :no_quorum}` when the cluster doesn't have enough nodes to reach consensus

## Examples

    iex> PaxosKV.put(:config, %{setting: "value"}, node: :node1@localhost)
    iex> PaxosKV.node(:config)
    {:ok, :node1@localhost}

# `now`

Returns the current time that can be used in the `until: ...` option.
The time is measured in milliseconds and is actually the system time.

# `pid`

Returns the pid the key-value pair is bound to. This is the pid set by the
`pid: p` option in `put/3`.

The function returns:
- `{:ok, pid}` when a pid is associated with the key
- `{:ok, default}` when the key is not found and the `default: d` option is provided
- `{:error, :not_found}` when the key is not registered and no default is provided
- `{:error, :no_quorum}` when the cluster doesn't have enough nodes to reach consensus

## Examples

    iex> pid = spawn(fn -> Process.sleep(1000) end)
    iex> PaxosKV.put(:session, "xyz", pid: pid)
    iex> PaxosKV.pid(:session)
    {:ok, pid}

# `put`

Stores the given value under the key in the collective memory (a KV store) of
the cluster.

    PaxosKV.put(key, value)
    PaxosKV.put(key, value, pid: self())

The function returns:
- `{:ok, value}` when consensus is reached for the key
- `{:error, :invalid_value}` when the value is invalid
- `{:error, :no_quorum}` when the cluster doesn't have enough nodes (default behavior)

Note: The returned `value` may not be the same as the `value` argument if another
process has already set a different value for the key. Once a key has a consensus
value, it cannot be changed.

## Examples

    iex> PaxosKV.put(:user_name, "Alice")
    {:ok, "Alice"}

    iex> PaxosKV.put(:counter, 42)
    {:ok, 42}

    # Setting a value with expiration time
    iex> PaxosKV.put(:session, "xyz", until: PaxosKV.now() + 60_000)
    {:ok, "xyz"}

## 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
* `key: k` -- keep the key-value pair as long as key `k` is present
* `until: u` -- keep the key-value pair until system time `u` (milliseconds)
* `no_quorum: :retry | :fail | :return` -- controls behavior when there's no quorum:
  - `:return` (default) -- returns `{:error, :no_quorum}`
  - `:retry` -- retries until quorum is reached (blocks indefinitely)
  - `:fail` -- throws an exception

To reach consensus, more than `cluster_size / 2` nodes must be part of the cluster.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
