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
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.
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.
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(node)
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.
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.
preferred_node_name(key, bucket \\ "riax")
Like preferred_node/2
, but returns only
the node's name.
ring_status()
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|
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}