September 25, 2013 by by name: "Drew Kerrigan"

An Elixir wrapper for riak-erlang-client.

What is Elixir?

From http://elixir-lang.org/:

Elixir is a functional, meta-programming aware language built on top of the Erlang VM. It is a dynamic language with flexible syntax and macro support that leverages Erlang’s abilities to build concurrent, distributed and fault-tolerant applications with hot code upgrades.

Ok… But what’s wrong with Erlang?

Absolutely nothing.

Some have made the argument that Elixir’s ruby-like syntax is better than Erlang’s, but I don’t buy that. Elixir is still a functional language just like Erlang, and if you don’t like the way things are done in one, you probably won’t like the other. The choice between Elixir and Erlang is a matter of preference in how you like to express ideas through code.

I love the simplicity of Erlang’s syntax and data types for a lot of problems. For instance it makes sending raw messages from process to process look nice and clean no matter what machine each process happens to be running on. Elixir allows you to call erlang functions natively and throws all kinds of syntactic sugar on top of it allowing you to make additional abstractions with macros and the like. The tools that Elixir provides also make it easier to over-engineer things because it essentially gives you unlimited options when deciding how to tackle a problem, but some people like that flexibility.

Back to the Riak Elixir Client: Usage

I’ll leave the setup instructions out of this post as they are available in the README. Let’s get right into some usage options.

I intentionally stuck to functional programming dispatch style for this implementation. I wrote a separate client called Elixiak that uses object-oriented style dispatch (which is generally frowned upon by the Elixir community from what I understand, but it’s still a matter of preference).

Connect to Riak via Protobufs

Riak.start
Riak.configure(host: '127.0.0.1', port: 8087)

The start function starts the OTP application, and configure sends a message to the OTP server running locally which starts the protobuf link with your Riak cluster.

CRUD operations

u = RObj.create(bucket: "user", key: "my_key", data: "Drew Kerrigan")
  |> Riak.put

u = Riak.find "user", "my_key"

u = u.data("Updated Data")
  |> Riak.put

Riak.delete "user", key

The |> takes the result of the previous function and passes it as the first argument to the next function. You can also pass u to Riak.put like so: Riak.put u

Bucket props

Riak.Bucket.put "user", [{:notfound_ok, false}])

{:ok, props} = Riak.Bucket.get "user"

Riak.Bucket.reset "user"

:ok is an Elixir atom, the equivalent of ok in Erlang.

User metadata

u = RObj.create(bucket: "user", key: "my_key", data: "Drew Kerrigan")
    |> RObj.put_metadata({"my_key", "my_value"})
    |> RObj.put_metadata({"my_key2", "my_value2"})
    |> Riak.put

Secondary indexes

u = RObj.create(bucket: "user", key: key, data: "Drew Kerrigan")
    |> RObj.put_index({:binary_index, "first_name"}, ["Drew"])
    |> RObj.put_index({:binary_index, "last_name"}, ["Kerrigan"])
    |> Riak.put

index = {:binary_index, "first_name"}
query = "Drew"
options = []

{keys, terms, continuation} = Riak.Index.query("user", index, query, options)

The call to Riak.Index.query above is a good example of pattern matching in Elixir. The variables keys, terms, and continuation are bound to values returned in that order as a tuple by the query function. terms and continuation are relatively new features of Riak, introduced in Riak 1.4. They allow for additional information about what terms were matched in your query as well as the ability to do pagination by specifying rows and start parameters in the options variable.

u = RObj.create(bucket: "user", key: "my_key", data: "Drew Kerrigan")
    |> RObj.put_link("my_tag", "user", "drew1")
    |> RObj.put_link("my_tag", "user", "drew2")
    |> Riak.put

Siblings

If you have the bucket property allow_mult set, you will likely encounter siblings, below is a quick example of resolving them.

user_value_list = Riak.find "user", key

if (is_list(user_value_list)) do
    Riak.resolve("user", key, 1)
end

The 1 parameter corresponds to the nth entry in the list of siblings contained in user_value_list.

Counters, Yokozuna, and Map/Reduce

The code for these features is written, but the riak-erlang-client functionality for counters and yokozuna is still in active development and therefore unstable, so I’m waiting a bit before endorsing their use with this client library.

Map/reduce functionality also exists, but I was unable to resolve a few bugs related to syntax with anonymous map and reduce functions. Feel free to try it out though, it should work fine with non-anonymous map/reduce functions in which the code already exists on your Riak nodes.

That’s it!

More detailed usage examples can be found in the tests: http://github.com/drewkerrigan/riak-elixir-client/blob/master/test/riak_test.exs

There will be another blog post on the way discussing the object-oriented style dispatch client library called Elixiak: http://github.com/drewkerrigan/elixiak