A durable key-value store on Slap.KV.Cluster. Rows are addressed by a
partition and a key, both non-empty binaries: the partition places the
row on a shard (Slap.KV.Cluster.shard_for(partition)), and within a
partition rows are in key order, so a partition can be scanned. There
are no scans across partitions.
Each row has a version, an integer that changes with every write to it.
put/4 and delete/3 take if_version: to write only if the row is at
that version (:absent for a row that must not exist), so a
read-modify-write can detect a concurrent change.
A write returns once it is durable, and reads return only durable rows:
a read never sees a write that could still be lost.
Pass cluster: to use a module defined with use Slap.KV.Cluster, otp_app: :my_app. Run one KV cluster per VM.
A write may be applied after its caller has stopped waiting for it (see
:timeout below). A write with a :deadline is not applied after it,
and a read with consistency: :linearizable sees every write applied
before it: together, they let a caller find out when a write it gave up
on can no longer happen.
Errors shared by all calls
Invalid row data and scan bounds return {:error, {:bad_request, reason}}.
Invalid control options (:consistency, :with_versions, :timeout,
:cluster) and unknown option names raise ArgumentError.
{:error, :unavailable}- the shard is stopping, moving or has no owner, or a write failed. A write that fails this way may still have been applied: read the row to find out rather than retry blindly.{:error, :timeout}- no reply within:timeout(default 30 s). A write may still be applied.{:error, :deadline_exceeded}- a write's:deadlinepassed before it was applied. It was not, and will not be, applied.{:error, {:bad_request, reason}}- invalid row data or scan bounds.
Summary
Functions
Deletes a row. Returns :ok once the deletion is durable, whether or not
the row existed. With if_version: version the row must be at version,
and with deadline: it must be applied by then (as for put/4).
Reads a row. Returns {:ok, %{value: value, version: version}}, or
{:ok, nil} when there is none.
Writes a row. Returns {:ok, version} once it is durable.
Lists a partition's rows in key order. Returns {:ok, %{rows: [{key, value}], cursor: cursor}}, where cursor is nil after the last page.
With with_versions: true, rows are {key, value, version}.
Types
The value and version returned by get/3.
@type key() :: binary()
@type page() :: %{rows: [scan_entry()], cursor: binary() | nil}
@type partition() :: binary()
A row returned by scan/2, with its key and optional version.
@type version() :: non_neg_integer()
Functions
Deletes a row. Returns :ok once the deletion is durable, whether or not
the row existed. With if_version: version the row must be at version,
and with deadline: it must be applied by then (as for put/4).
Reads a row. Returns {:ok, %{value: value, version: version}}, or
{:ok, nil} when there is none.
With consistency: :linearizable, the read goes through the row's
partition writer, behind the writes it has received, and returns once
every write it has applied is durable and a write of its own confirms
that its node still owns the shard. Such a read sees every write that
was applied before it, acknowledged or not; it costs a write. The
default, :durable, reads what is durable without waiting, and may miss
a write that is applied but not yet acknowledged.
Writes a row. Returns {:ok, version} once it is durable.
With if_version: version the row must be at version, and with
if_version: :absent there must be no row; otherwise the result is
{:error, {:conflict, current}}, where current is the row's version,
or nil if there is none.
With deadline: ms (a system time, System.os_time(:millisecond)) the
write is applied only if the row's partition writer gets to it before
then, by its node's clock; otherwise the result is {:error, :deadline_exceeded}. So once a clock reads ms plus the largest
difference between nodes' clocks, the write can no longer be applied.
Lists a partition's rows in key order. Returns {:ok, %{rows: [{key, value}], cursor: cursor}}, where cursor is nil after the last page.
With with_versions: true, rows are {key, value, version}.
Options:
:prefix- only keys that start with it.:gte,:lt- only keys in this range.:limit- rows per page, 1 to 1,000 (default 100).:cursor- thecursorof the previous page, to continue after it.:with_versions- include each row's version (defaultfalse).
Each page is a separate read of the durable rows: rows written between pages appear in later pages if their keys come later.