FakeRiak.SimpleStore (fake_riak v0.3.0)

Copy Markdown View Source

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/2 take (bucket, key) (keys/1 takes just (bucket), buckets/0 takes nothing) and are used by FakeRiak.Riak;
  • a bucketless space — put/2, get/1, delete/1 take just (key) and are used by FakeRiak.Redis, FakeRiak.Etcd and FakeRiak.Memcached;
  • a counter space — counter_update/3 and counter_get/2 take (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/4 and index_query/3 back 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 :replace put overwrites the key's entries, an :append (sibling) put unions the new entries in. index_query/3 answers {:eq, term} and {:range, min, max} lookups, comparing numerically for _int index names and as binaries for _bin ones.
  • a datatype space — dt_fetch/3 and dt_update/4 back Riak's CRDT data types (counter, set, gset — map and hll are out of scope). It is keyed by (type, bucket, key), where type is the bucket-type name that namespaces the data, and holds a {datatype, value} pair: a signed integer for a :counter, a MapSet for a :set/:gset. dt_update/4 infers 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

buckets()

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

counter_get(bucket, key)

Read the counter at (bucket, key): {:ok, value}, or {:error, :not_found} when it has never been updated.

counter_update(bucket, key, amount)

Add amount (which may be negative) to the counter at (bucket, key), defaulting an absent counter to 0, and return the new signed value.

delete(key)

delete(bucket, key)

dt_fetch(type, bucket, key)

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

dt_update(type, bucket, key, op)

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

get(key)

get(bucket, key)

index_query(bucket, index, query)

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.

init(_)

Callback implementation for GenServer.init/1.

keys(bucket)

put(key, value)

put(bucket, key, value, mode \\ :replace)

put_indexes(bucket, key, entries, mode)

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.

start_link(_)