ram (ram v0.1.0) View Source

Exposes all of the Key Value store APIs.

Ram doesn't have to run on every node of the Erlang cluster. You may start Ram only on those nodes where you need it. When started, Ram creates a logical overlay network running on top of the Erlang distribution cluster.

Nodes where Ram runs form a subcluster: they will synchronize data between themselves, and themselves only. All of the data is replicated on every node of the subcluster.

Quickstart

Elixir

  iex(1)> :ram.get("key")
  :undefined
  iex(2)> :ram.put("key", "value")
  :ok
  iex(3)> :ram.get("key")
  "value"

Erlang

  1> ram:get("key").
  undefined
  2> ram:put("key", "value").
  ok
  3> ram:get("key").
  "value"

Internals

Ram operations are Atomic (take effect on all nodes involved, or on none of the nodes), Consistent (the data is the same across all nodes) and Isolated (operations on different nodes in a network do not interfere with each other). They are not Durable since Ram is an in-memory only database.

To do so, every operation creates a global lock with global:trans/4 in the caller process, which then runs a transaction with a 2-phase commit protocol. If transactions fail, they raise error({commit_timeout, {bad_nodes, BadNodes}}) where BadNodes is the list of subcluster nodes where the transaction could not complete.

Conflict Resolution

In case of net splits or bad network conditions, a specific Key might get put simultaneously on two different nodes. When this happens, the cluster experiences a Key conflict.

Ram will resolve this conflict by choosing a single Value. By default, Ram keeps track of the time when a registration takes place with erlang:system_time/0, compares values between conflicting processes and keeps the one with the higher value (the Value that was put more recently). This is a very simple mechanism that can be imprecise, as system clocks are not perfectly aligned in a cluster.

Link to this section Summary

Functions

Deletes a Key.

Looks up a Key.

Equivalent to get(Key, undefined).

Returns the Key's Value or Default if the Key is not found.

Puts a Value for a Key.

Starts Ram manually.

Stops Ram manually.
Returns the nodes of Ram's subcluster.

Atomically updates a Key with the given function.

Link to this section Functions

Specs

delete(Key :: term()) -> ok.
Deletes a Key.

Specs

fetch(Key :: term()) -> {ok, Value :: term()} | error.

Looks up a Key.

Returns error if the Key is not found.

Specs

get(Key :: term()) -> Value :: term().

Equivalent to get(Key, undefined).

Specs

get(Key :: term(), Default :: term()) -> Value :: term().

Returns the Key's Value or Default if the Key is not found.

Examples

Elixir

  iex(1)> :ram.get("key")
  :undefined
  iex(2)> :ram.get("key", "default")
  "default"
  iex(3)> :ram.put("key", "value")
  :ok
  iex(4)> :ram.get("key")
  "value"

Erlang

  1> ram:get("key").
  undefined
  2> ram:get("key", "default").
  "default"
  3> ram:put("key", "value").
  ok
  4> ram:get("key").
  "value"

Specs

put(Key :: term(), Value :: term()) -> ok.
Puts a Value for a Key.

Specs

start() -> ok.

Starts Ram manually.

In most cases Ram will be started as one of your application's dependencies, however you may use this helper method to start it manually.

Specs

stop() -> ok | {error, Reason :: term()}.
Stops Ram manually.

Specs

subcluster_nodes() -> [node()] | not_running.
Returns the nodes of Ram's subcluster.
Link to this function

update(Key, Default, Fun)

View Source

Specs

update(Key :: term(), Default :: term(), function()) -> ok.

Atomically updates a Key with the given function.

If Key is found then the existing Value is passed to the fun and its result is used as the updated Value of Key. If Key is not found, Default is put as the Value of Key. The Default value will not be passed through the update function.

Examples

Elixir

  iex(1)> update_fun = fn existing_value -> existing_value * 2 end
  #Function<44.65746770/1 in :erl_eval.expr/5>
  iex(2)> :ram.update("key", 10, update_fun)
  ok
  iex(3)> :ram.get("key")
  10
  iex(4)> :ram.update("key", 10, update_fun)
  ok
  iex(5)> :ram.get("key")
  20

Erlang

  1> UpdateFun = fun(ExistingValue) -> ExistingValue * 2 end.
  #Fun<erl_eval.44.65746770>
  2> ram:update("key", 10, UpdateFun).
  ok
  3> ram:get("key").
  10
  4> ram:update("key", 10, UpdateFun).
  ok
  5> ram:get("key").
  20