The shared in-memory key-value store, a single GenServer backing every
endpoint.
It holds three independent keyspaces so several protocols can share one process:
- a bucketed space —
put/3,4,get/2,delete/2take(bucket, key)(keys/1takes just(bucket),buckets/0takes nothing) and are used byFakeRiak.Riak; - a bucketless space —
put/2,get/1,delete/1take just(key)and are used byFakeRiak.Redis,FakeRiak.EtcdandFakeRiak.Memcached; - a counter space —
counter_update/3andcounter_get/2take(bucket, key)and hold one signed integer each, backing Riak's legacy PBC counters (RpbCounterUpdateReq/RpbCounterGetReq). It is kept apart from the bucketed object space: a counter is a single number, not a list of sibling binaries. - an index space —
put_indexes/4andindex_query/3back Riak's secondary indexes (2i). Each(bucket, key)holds a set of{index_name, term}entries, attached to the object at put time and dropped when the key is deleted. A put's merge mode carries over: a:replaceput overwrites the key's entries, an:append(sibling) put unions the new entries in.index_query/3answers{:eq, term}and{:range, min, max}lookups, comparing numerically for_intindex names and as binaries for_binones. - a datatype space —
dt_fetch/3anddt_update/4back Riak's CRDT data types (counter, set, gset — map and hll are out of scope). It is keyed by(type, bucket, key), wheretypeis the bucket-type name that namespaces the data, and holds a{datatype, value}pair: a signed integer for a:counter, aMapSetfor a:set/:gset.dt_update/4infers the datatype from the op it is given and grows the value; set removes are best-effort (applied directly, since the fake keeps no real causal context), while a gset only ever grows.
The bucketed space models Riak's siblings: each key holds a list of
values, so get/2 replies {:ok, values} with a non-empty list. put/4
takes a merge mode — :append adds the value as a new sibling (byte-identical
values are deduplicated, as real Riak does), while :replace collapses the
key to a single value, the way resolving with a vector clock does. put/3
defaults to :replace. The bucketless space stays strictly last-write-wins:
its get/1 replies {:ok, value} with a single value.
In both spaces get replies {:error, reason} when absent, and
put/delete reply :ok (with delete returning {:error, reason} when
the key is absent). The public functions run in the caller's connection
process, so when --verbose is set they log each operation, attributed to
that connection's protocol.
Summary
Functions
Returns a specification to start this module under a supervisor.
Read the counter at (bucket, key): {:ok, value}, or {:error, :not_found}
when it has never been updated.
Add amount (which may be negative) to the counter at (bucket, key),
defaulting an absent counter to 0, and return the new signed value.
Read the CRDT data type at (type, bucket, key) (the bucket type namespaces
the data): {:ok, {datatype, value}}, or {:error, :not_found} when the key
has never been updated. datatype is :counter, :set or :gset; value
is a signed integer (counter) or a MapSet (set/gset).
Apply a CRDT operation op to the data type at (type, bucket, key) and
return the new {datatype, value}. op is one of {:counter, increment},
{:set, {adds, removes}} or {:gset, adds}.
Query the secondary index index in bucket. query is {:eq, term} or
{:range, min, max}; _int indexes compare numerically, _bin ones as
binaries. Returns the matching {term, key} pairs, sorted and deduplicated.
Callback implementation for GenServer.init/1.
Attach secondary-index entries (a list of {index_name, term} tuples) to
the object at (bucket, key). :replace overwrites the key's entries the way
a vclock-resolving put does; :append unions them in the way a sibling put
does. Entries are cleared when the key is deleted.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
Read the counter at (bucket, key): {:ok, value}, or {:error, :not_found}
when it has never been updated.
Add amount (which may be negative) to the counter at (bucket, key),
defaulting an absent counter to 0, and return the new signed value.
Read the CRDT data type at (type, bucket, key) (the bucket type namespaces
the data): {:ok, {datatype, value}}, or {:error, :not_found} when the key
has never been updated. datatype is :counter, :set or :gset; value
is a signed integer (counter) or a MapSet (set/gset).
Apply a CRDT operation op to the data type at (type, bucket, key) and
return the new {datatype, value}. op is one of {:counter, increment},
{:set, {adds, removes}} or {:gset, adds}.
Query the secondary index index in bucket. query is {:eq, term} or
{:range, min, max}; _int indexes compare numerically, _bin ones as
binaries. Returns the matching {term, key} pairs, sorted and deduplicated.
Callback implementation for GenServer.init/1.
Attach secondary-index entries (a list of {index_name, term} tuples) to
the object at (bucket, key). :replace overwrites the key's entries the way
a vclock-resolving put does; :append unions them in the way a sibling put
does. Entries are cleared when the key is deleted.