30 September 2013

Riak is an opensource distributed non-relational database.

In Riak, you do not have tables with fixed schema (‘columns’) to store data. Instead you have buckets. And buckets hold keys and their values. The values may be of different types.

If you are on a Mac with Homebrew, then installing Riak is one command away:

brew install riak

For everything else, checkout the Riak documentation

Start Riak first (refer the documentation on how to do it). On my mac, running riak start will start Riak on the default port.

Initial setup

Create your Elixir project

$ mix new riak_example --sup

The --sup switch tells mix to creates a supervisor also.

Add the Riak erlang client to project dependencies

Basho provides an Erlang client library to connect to Riak - riak-erlang-client. We’ll use that to connect to Riak from Elixir. After all, Elixir runs on the Erlang VM.

Add the Riak erlang client to the list of dependencies in the deps function in the mix.exs file of your project.

defp deps do
  [ {:riakc, github: "basho/riak-erlang-client"} ]
end

Why did I use :riakc?

If you are from any other language/ecosystem, you might be used to repositories having the same name as the library name. When you include an erlang library, check the name of the *.app or *.app.src file in the src directory of the Erlang library. In the Riak erlang client’s case, the file within the src dir is riakc.app.src. That is the OTP appication specification file.

Playing with Riak from the Elixir console

Let’s play with Riak from the project’s console. Start the console with iex -S mix.

You’ll be using the following two Erlang modules:

  • riakc_pb_socket - Used to perform operations on the database.

  • riakc_obj - Used to wrap and unwrap Riak objects.

Starting Riak

Start the riak client. (Note the use of single quotes)

{ok, pid} = :riakc_pb_socket.start_link('127.0.0.1', 8087)

If Riak and the Riak erlang client have started, it should respond to pings

:riakc_pb_socket.ping(pid)  # => pong

Storing values

To store details of a student, in the students bucket, you need to first wrap your data in a Riak object

{:ok, student} = :riakc_obj.new("students", :undefined, [name: "akash manohar"])
  • The first argument is the name of the bucket to store the data in.
  • The second argument is the key you want to assign to the value. If you pass :undefined, Riak will assign a key.
  • The third argument is the value you want to store for this key.

Note: If you pass a key yourself instead of :undefined, then Riak client will only return :ok

Next, store the student object in the database

{:ok, stored_obj} :riakc_pb_socket.put(pid, student)

When you store the value pair in the database, the Riak client returns an object, which has the key of the value stored.

:riakc_obj.key(stored_obj)   # => "LHBFxjIC7lh4hvPubrSYTgixKYM"

It is also possibe to get the bucket name from the key

:riakc_obj.bucket(stored_obj)  # => "students"

Getting values from the database

(Thanks to @josevalim for helping out with this stuff)

Fetch the value of the key stored in the database.

{:ok, student_obj} = :riakc_pb_socket.get(pid, "students", "LHBFxjIC7lh4hvPubrSYTgixKYM")

Whatever is stored in student_obj might look like gibberish on first sight. What is returned is Riak’s representation of the value you stored and not the value of the key straight away. Try inspecting the student_obj and you’ll see for yourself.

Now lets get the value stored for the key from the Riak object using the :riakc_obj Erlang module.

values = :riakc_obj.get_values(student_obj)

# Oh yes this is what you'll get
[<<131, 108, 0, 0, 0, 1, 104, 2, 100, 0, 4, 110, 97, 109, 101, 109, 0, 0, 0, 13, 97, 107, 97, 115, 104, 32, 109, 97, 110, 111, 104, 97, 114, 106>>]

That is a list of binaries. The first and only element in the list is the value you stored.

# Take the head of the list
# and convert it to an erlang term
values |> hd |> binary_to_term

# And this is what you'll get
[name: "akash manohar"]

Awesomeness ~! Your first Riak database fetch from Elixir

We are using binary_to_term because we are storing Erlang binaries. Riak also supports storing and indexing JSON and a few other formats. Ideally, I would choose to store JSON, because that way any programs written in other languages, accessing the database, might find it easier.

Updating data

First get the Riak object of the value you want to update.

{:ok, student_obj} = :riakc_pb_socket.get(pid, "students", "LHBFxjIC7lh4hvPubrSYTgixKYM")

Update the student object and store it

updated_student = :riakc_obj.update_value(student_obj, [name: "kumar"])
:riakc_pb_socket.put(pid, updated_student, [:return_body])

Now try getting the student again and check it’s value. You’ll have the new value.

Deleting data

Easy peasy ~! You’ll need the bucket name and the key

:riakc_pb_socket.delete(pid, "students", "LHBFxjIC7lh4hvPubrSYTgixKYM")

Using the Riak client in your OTP application

We’ll write a simple app to list, create, get, update and delete students. You might get an idea on how to use Erlang/Elixir libraries in your project and add them to the supervision tree.

We are using the same app, generated at the beginning of this post. It already has generated a supervisor for us. We’ll have to write some code to tell the supervisor to start and monitor the Riak client.

Now in our Eixir app, the following will be the contents of lib/riak_example/supervisor.ex.

defmodule RiakExample.Supervisor do
  use Supervisor.Behaviour

  def start_link do
    # We'll name this supervisor as "riak_client_sup"
    :supervisor.start_link({:local, :riak_client_sup}, __MODULE__, [])
  end

  def init([]) do
    # We'll ask OTP to set the ID of the riak client as :riak_client
    # We won't be using the ID,
    # but if the supervisor is supervising
    # multiple processes, then it'll come handy

    children = [
      worker(:riakc_pb_socket, ['127.0.0.1', 8087], id: :riak_client)
    ]

    supervise(children, strategy: :one_for_one)
  end
end

Add the following to lib/riak_example.ex.

defmodule RiakExample do
  use Application.Behaviour

  # Start the supervisor
  def start(_type, _args) do
    RiakExample.Supervisor.start_link
  end

  def ping do
    :riakc_pb_socket.ping(riak_client)
  end

  def list do
    # Get a list of keys stored
    :riakc_pb_socket.list_keys(riak_client, "students")
  end

  def create(details) do
    obj = :riakc_obj.new("students", :undefined, details)
    :riakc_pb_socket.put(riak_client, obj)
  end

  def get(key) do
    {:ok, obj} = :riakc_pb_socket.get(riak_client, "students", key)
    :riakc_obj.get_values(obj) |> hd |> binary_to_term
  end

  def update(key, value) do
    {:ok, obj} = :riakc_pb_socket.get(riak_client, "students", key)
    updated_obj = :riakc_obj.update_value(obj, value)
    :riakc_pb_socket.put(riak_client, updated_obj, [:return_body])
  end

  def delete(key) do
    :riakc_pb_socket.delete(riak_client, "students", key)
  end


  # This is a helper method to get the Riak client's pid
  # from the supervisor
  defp riak_client do
    # :supervisor.which_children() will return
    # a list of children the supervisor is supervising
    # We are supervising only one process,
    # So we'll pass it to the hd() function and get the first element

    {_, client_pid, _, _} = :supervisor.which_children(:riak_client_sup)
                              |> hd
    client_pid
  end
end

Start the Elixir console with iex -S mix and you’ll be able to play with the app.

$ iex -S mix
iex(1)> RiakExample.list
{:ok,
 ["LHBFxjIC7lh4hvPubrSYTgixKYM", "TuEfE2DOOfz8AkPpdzLmdPYUuVK",
  "PPQuKZsyHWVPSbs3rQQVWW9nyTe"]}

iex(2)> RiakExample.get("LHBFxjIC7lh4hvPubrSYTgixKYM")
[name: "kumar"]

iex(3)> RiakExample.update("LHBFxjIC7lh4hvPubrSYTgixKYM", name: "P Kumar")
# -- will dispay riak object representation --

iex(4)> RiakExample.get("LHBFxjIC7lh4hvPubrSYTgixKYM")
[name: "P Kumar"]

iex(5)> RiakExample.delete("LHBFxjIC7lh4hvPubrSYTgixKYM")
:ok

Notes

I’m loving Elixir. I’ve been playing with it for a couple weeks now. If you are interested in such stuff, I’m @HashNuke on twitter.

blog comments powered by Disqus