This document details aspects of the Riak client interfaces and can be used as a general guide for understanding how to interact with Riak and how to implement a compliant client library or application. Below are some high-level recommendations for implementing well-behaved clients.
Hide Transport Details
Although Riak's two interfaces do not have complete feature-parity, client libraries should make an effort to hide implementation details of the protocols from applications, presenting a uniform interface. This will reduce the amount of code changes needed to select a different transport mechanism for operational reasons.
As described in the Eventual Consistency document, there are many scenarios that can result in temporary inconsistency, which may cause the appearance of stale data or sibling objects. To react appropriately to unexpected or unsuccessful results, clients should retry requests a small number of times before failing to the caller. For example, when fetching a key that might be inconsistent among its replicas, this gives read repair a chance to update the stale copies. Retries also may give the client an opportunity to reconnect to the Riak node (or a different node in the cluster) when the original connection is lost for whatever reason. A number of operations in Riak are idempotent and thus can be retried without side-effects.
In order to give applications the opportunity to recover from conflicting or partitioned writes to a key, Riak can be configured to present the client multiple versions of the value, also known as siblings. It then becomes the responsibility of the application to resolve those siblings in a way that is meaningful to the application domain. Clients should provide a way to encapsulate resolution behavior such that it can be run automatically when sibling values are detected, potentially multiple times in the course of a fetch or storage operation. Without sibling resolution, the number of stored versions will continually grow, resulting in degraded performance across the cluster in the form of extremely high per-operation latencies or apparent unresponsiveness.
Read-before-Write & Causal Context
Riak will return an encoded causal context with every “fetch” or
“read” request that does not result in a
not found response. This
vector clock tells Riak how to resolve concurrent writes, essentially
representing the “last seen” version of the object to which the client
made modifications. In order to prevent sibling explosion, clients should always use this vector clock when
updating an object. Therefore, it is essential that keys are fetched
before being written (except in the case where Riak selects the key or
there is a priori knowledge that the key is new). Client libraries
that make this automatic will reduce operational issues by limiting
sibling explosion. Clients may also choose to perform automatic
Sibling Resolution on
Discourage Expensive Operations
A number of operations (e.g. HTTP List Keys), while useful, are expensive to run in production. A well-behaved client should expose all functionality, but warn the developer when they choose to perform those expensive operations.
In most cases — especially when using the PBC API, which tends to
use small messages — clients should set the
TCP_NODELAY flag on
opened socket connections to Riak, which disables Nagle's
profiles having a minimum of 40 milliseconds often indicate the presence
of Nagle on either end of the connection. If the client application has
significant round-trip latency to the Riak cluster, disabling Nagle will
have little effect, but for well-connected clients it can significantly