Riax (riax v0.1.0)

This module provides the set of functions to communicate with your VNode implementation. To implement a VNode, check the Riax.VNode module documentation. For an example on how to setup and use Riax, check the tutorial section.

Link to this section Summary

Functions

Same as sync_command/3 but does not block the Master VNode's process. That is, it lets the Master VNode handle multiple requests concurrently.

Works like sync_command/3, but does not generate a response from the VNode that handles the request. In that way, it's similar to how GenServer.cast/2 works. Following sync_comand/3's example, its usage would be like this

Execute a command across every available VNode. This will start the coverage FSM (implemented in Riax.Coverage.Fsm), via the coverage supervisor, and gather the results from every VNode. Be careful, coverage commands can be quite expensive. The results are gathered as a list of 3 tuple elements: {partition, node, data}

Join the running node with the given argument node. This will automatically trigger the handoff - the nodes will start distributing partitions (and therefore, keys) between them. See the ring_status/0 example.

This is actually the head of the Active Preference List: a list of the available VNodes to handle a given request. We always use the first available one.

Like preferred_node/2, but returns only the node's name.

Prints the ring status. The ring is, basically, a representation of the partitioned keys over nodes. Here's a visual representation of said ring

Use the VNode master to send a synchronous command to the VNode that receives the key. Keep in mind this blocks the Master VNode's process, and will not be able multiple requests concurrently.

Link to this section Functions

Link to this function

async_command(key, bucket \\ "riax", command)

Same as sync_command/3 but does not block the Master VNode's process. That is, it lets the Master VNode handle multiple requests concurrently.

Link to this function

cast_command(key, bucket \\ "riax", command)

Works like sync_command/3, but does not generate a response from the VNode that handles the request. In that way, it's similar to how GenServer.cast/2 works. Following sync_comand/3's example, its usage would be like this:

  iex(dev1@127.0.0.1)3> Riax.cast_command(1, "riax", {:ping, 1})
  :ok
  13:36:19.742 [debug] Received ping command!

As you can see, the VNode does handle the request and logs it, but we only get an :ok as return value, like GenServer.cast/2.

Link to this function

coverage_command(command, timeout \\ 5000)

Execute a command across every available VNode. This will start the coverage FSM (implemented in Riax.Coverage.Fsm), via the coverage supervisor, and gather the results from every VNode. Be careful, coverage commands can be quite expensive. The results are gathered as a list of 3 tuple elements: {partition, node, data}

parameters

Parameters:

  • command: Command for the VNode, should match the first argument of a
         `handle_coverage/4` definition from your VNode.
  • timeout: timeout in microseconds, 5000 by default.

example

Example:

Let's say we want to call this function:

def handle_coverage(:keys, _key_spaces, {_, req_id, _}, state = %{data: data}) do
  keys = Map.keys(data)
  {:reply, {req_id, keys}, state}
end

Then, we must do:

iex(dev2@127.0.0.1)6> Riax.coverage_command(:keys)
  14:25:33.084 [info] Starting coverage request 74812649 keys
  {:ok,
  [
  {1027618338748291114361965898003636498195577569280, :"dev2@127.0.0.1", ''},
  {936274486415109681974235595958868809467081785344, :"dev2@127.0.0.1", [22]},
  {1415829711164312202009819681693899175291684651008, :"dev2@127.0.0.1", 'E'},
  {1392993748081016843912887106182707253109560705024, :"dev2@127.0.0.1", 'AV'},
  {959110449498405040071168171470060731649205731328, :"dev2@127.0.0.1", 'CZ'},
  ...
  ]

Join the running node with the given argument node. This will automatically trigger the handoff - the nodes will start distributing partitions (and therefore, keys) between them. See the ring_status/0 example.

Link to this function

preferred_node(key, bucket \\ "riax")

This is actually the head of the Active Preference List: a list of the available VNodes to handle a given request. We always use the first available one.

The VNode is represented and returned as: {index, node_name}. The first element denotes the first key in the partition the vnode is responsible for (as an integer), and the second element refers to the (physical) node the vnode is running on.

parameters

Parameters:

  • key: can be any erlang term, but it is recommended to use numbers or strings.
  • bucket: is the name of the bucket for this key. A bucket is a "namespace" for a given Key. Check this for more.
Link to this function

preferred_node_name(key, bucket \\ "riax")

Like preferred_node/2, but returns only the node's name.

Prints the ring status. The ring is, basically, a representation of the partitioned keys over nodes. Here's a visual representation of said ring

example

Example:

Join 2 running nodes and print the ring, to see the key distribution (handoff) result:

iex(dev2@127.0.0.1)3> Riax.ring_status
  ==================================== Nodes ====================================
  Node a: 64 (100.0%) dev2@127.0.0.1
  ==================================== Ring =====================================
  aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|aaaa|
  :ok
iex(dev2@127.0.0.1)13> Riax.join('dev1@127.0.0.1')
  13:51:21.258 [debug] Handoff starting with target: {:hinted, {913438523331814323877303020447676887284957839360, :"dev1@127.0.0.1"}}
  ...
iex(dev2@127.0.0.1)6> Riax.ring_status
  ==================================== Nodes ====================================
  Node a: 64 (100.0%) dev1@127.0.0.1
  Node b: 0 (  0.0%) dev2@127.0.0.1
  ...
# After a little while, run the command again
# to check the ring status.
iex(dev2@127.0.0.1)11> Riax.ring_status
  ==================================== Nodes ====================================
  Node a: 32 ( 50.0%) dev1@127.0.0.1
  Node b: 32 ( 50.0%) dev2@127.0.0.1
  ==================================== Ring =====================================
  abba|abba|abba|abba|abba|abba|abba|abba|abba|abba|abba|abba|abba|abba|abba|abba|
Link to this function

sync_command(key, bucket \\ "riax", command)

Use the VNode master to send a synchronous command to the VNode that receives the key. Keep in mind this blocks the Master VNode's process, and will not be able multiple requests concurrently.

parameters

Parameters:

  • key: Can be any erlang term, but it is recommended to use a number or a binary. This is used to determine on which partition will end up, and therefore which VNode handles the command.
  • bucket: Is the name of the bucket for this key.
  • command: The command to send to the VNode. This will try to match with the defined handle_command/3 clause in the VNode module.

example

Example:

Let's say we have this function in our VNode module:

def handle_command({:ping, v}, _sender, _state) do
  Logger.debug("Received ping command!", state)
  {:reply, {:pong, v + 1, node(), partition}, state}
end

We can interact with it like this:

iex(dev1@127.0.0.1)> Riax.sync_command(1, "riax", {:ping, 1})
  13:13:08.004 [debug] Received ping command!
  {:pong, 2, :"dev1@127.0.0.1", 822094670998632891489572718402909198556462055424}