Skip to content

Storing data in riak

broach edited this page Apr 24, 2012 · 58 revisions

First, a word or two about Riak, CAP Theorem and eventual consistency.

Unless you're already familiar with CAP Theorem and eventual consistency, taking the time to read through at least The Riak Fast Track would be well worth your while.

It's ok, we'll wait.

Ok! Now that you've read through that and understand that Riak is a system that favors AP with eventual C, this might make some sense to you.

Storing data in Riak with the Java client.

In Riak data is stored in buckets. Those buckets have a number of options and tunable parameters, one of which is whether or not to allow sibling records. By default, a bucket does not allow sibling creation. The Riak Java client is somewhat built around this in that at the most basic level, you can simply say "store this data using this key" and anything that is currently in Riak referenced by that key will be overwritten. There are, however, some issues with attempting to do that.

If you have any type of contention/concurrency where multiple threads or processes are doing read/modify/write operations on those key/values, you are likely to lose writes if the operations interleave. One will overwrite the other. At that point you need to enable siblings and deal with conflict resolution.

With that in mind, the following basic examples illustrate using Riak with the default bucket options and just storing some data.

Basic Example #1: Store a String
Basic Example #2: Store a POJO
Basic Example #3: Changing query parameters

For a more detailed example of how you would store data in Riak in an environment with concurrency, jump down to the Advanced Examples section.

## Basic Store, data is a String

Using the Bucket.store(String, String) method, your String is stored in Riak as bytes representing UTF-8 text

public class App
{
    import com.basho.riak.client.IRiakClient;
    import com.basho.riak.client.IRiakObject;
    import com.basho.riak.client.RiakException;
    import com.basho.riak.client.RiakFactory;
    import com.basho.riak.client.bucket.Bucket;

    public static void main(String[] args) throws RiakException
    {
        String myData = "This is my data";
        riakClient = RiakFactory.httpClient();
        Bucket myBucket = riakClient.fetchBucket("TestBucket").execute();
        myBucket.store("TestKey", myData).execute();
        riakClient.shutdown();
    }
}
## Store POJO (serialized to JSON) By passing a POJO to the Bucket.store(String, T) method, your POJO is serialized to JSON using the Jackson library and stored in Riak as UTF-8 text. ```java public class App { class Pojo { public String foo; public String bar; public int foobar; }
import com.basho.riak.client.IRiakClient;
import com.basho.riak.client.IRiakObject;
import com.basho.riak.client.RiakException;
import com.basho.riak.client.RiakFactory;
import com.basho.riak.client.bucket.Bucket;

public static void main(String[] args) throws RiakException
{
    Pojo myPojo = new Pojo();
    myPojo.foo = "My foo data";
    myPojo.bar = "My Bar data";
    myPojo.foobar = 5;

    riakClient = RiakFactory.httpClient();
    Bucket myBucket = riakClient.fetchBucket("TestBucket").execute();
    myBucket.store("TestKey", myPojo).execute();

    riakClient.shutdown();
}

}

<a name="basicexample3"/>
## Store data, changing query parameters for just this request
To override the default parameters in the Bucket, you can specify them prior to calling the execute() method. 
```java
public class App
{
    import com.basho.riak.client.IRiakClient;
    import com.basho.riak.client.IRiakObject;
    import com.basho.riak.client.RiakException;
    import com.basho.riak.client.RiakFactory;
    import com.basho.riak.client.bucket.Bucket;
    import com.basho.riak.client.cap.Quora;
    import com.basho.riak.client.operations.StoreObject;

    public static void main(String[] args) throws RiakException
    {
        riakClient = RiakFactory.httpClient();
        Bucket myBucket = riakClient.fetchBucket("TestBucket").execute();
        StoreObject<IRiakObject> storeObject = myBucket.store("TestKey", "TestData");
        storeObject.w(Quora.ONE).pw(Quora.ONE).execute();
        riakClient.shutdown();
    }
}

# The Hard Way ## Eventual Consistency; Resolvers, Mutators, and Converters

In most environments, you're going to configure your buckets to allow siblings and write the code that deals with them. There are three Interfaces you're going to be using:

  • ConflictResolver<T>
    This Interface is used to resolve sibling records returned by Riak
  • Mutation<T>
    This interface is used to modify an object in Riak
  • Converter<T>
    This interface is used to serialize/deserialize data to/from Riak

One thing worth noting is that the current IRiakClient and its various interfaces aren't likely what you're used to if you've used other datastores' APIs. When using the above classes the current Java client design expects your entire read/modify/write cycle to be encapsulated entirely within the store operation. For more details see the Houston, we have a problem section on the fetching data from Riak page. If you do not wish to completely encapsulate the read/modify/write inside the store operation, see Storing previously fetched and modified data below. This will be addressed in a future version of the Java client.

The following diagram outlines the anatomy of a read/modify/write cycle using the Bucket interface and the StoreObject it returns:

### Figure 1 ![StoreObject anatomy](http://dl.dropbox.com/u/74693818/RJC-store-v2.png)

There are four versions of the store() method available in the Bucket interface:

  • StoreObject<IRiakObject> store(String key, byte[] value)
  • StoreObject<IRiakObject> store(String key, String value)
  • <T> StoreObject<T> store(String key, T o)
  • <T> StoreObject<T> store(T o)

The first two are only useful if you want to overwrite anything currently in Riak associated with the key you're passing in. Be aware, however, that there is a caveat. An anonymous Mutation instance is created and used. It replaces the data portion of whatever is currently stored in Riak, but not links, secondary indexes, or user metadata. If you want to overwrite everything you will need to supply a custom Converter that does so.

The second two are actually what you will most likely use if you are performing a read/modify/write cycle. As noted in figure 1 above, the interface is slightly clunky in that the object being passed in is going to be discarded when you supply the Mutation; it's only used to infer the type. The fourth version will extract the key from the object being passed in before doing so by referencing a String field annotated with @RiakKey.

## Storing previously fetched and modified data _(more to come)_