Conflict resolution is an inherent part of nearly any Riak application, whether or not the developer knows it.
There are basically 6 distinct approaches for dealing with conflicts in Riak, and well-written applications will typically use a combination of these strategies depending on the nature of the data.conflict-tuning
conflict-tuning. If each bucket has its own conflict resolution ↩
strategy, requests against that bucket can be tuned appropriately. For an example, see [Tuning for immutable data].
And, as of Riak 2.0, strong consistency can be used to avoid conflicts (but as we'll discuss below there are significant downsides to doing so).
Prior to Riak 2.0, the default behavior was for Riak to resolve
siblings by default (see Tuning parameters for the parameter
allow_mult
). With Riak 2.0, the default behavior changes to
retaining siblings for the application to resolve, although this will
not impact legacy Riak applications running on upgraded clusters.
For some use cases, letting Riak pick a winner is perfectly fine, but make sure you're monitoring your system clocks and are comfortable losing occasional (or not so occasional) updates.
It has always been possible to define data types on the client side to merge conflicts automatically.
With Riak 1.4, Basho started introducing distributed data types (formally known as CRDTs, or conflict-free replicated data types) to allow the cluster to resolve conflicting writes automatically. The first such type was a simple counter; Riak 2.0 adds sets and maps.
These types are still bound by the same basic constraints as the rest of Riak. For example, if the same set is updated on either side of a network split, requests for the set will respond differently until the split heals; also, these objects should not be allowed to grow to multiple megabytes in size.
As of Riak 2.0, it is possible to indicate that values should be managed using a consensus protocol, so a quorum of the servers responsible for that data must agree to a change before it is committed.
This is a useful tool, but keep in mind the tradeoffs: writes will be slower due to the coordination overhead, and Riak's ability to continue to serve requests in the presence of network partitions and server failures will be compromised.
For example, if a majority of the primary servers for the data are unavailable, Riak will refuse to answer read requests if the surviving servers are not certain the data they contain is accurate.
Thus, use this only when necessary, such as when the consequences of conflicting writes are painful to cope with. An example of the need for this comes from Riak CS: because users are allowed to create new accounts, and because there's no convenient way to resolve username conflicts if two accounts are created at the same time with the same name, it is important to coordinate such requests.
Resolving conflicts when data is being rapidly updated can feel Sysiphean.
It's always possible that two different clients will attempt to resolve the same conflict at the same time, or that another client will update a value between the time that one client retrieves siblings and it attempts to resolve them. In either case you may have new conflicts created by conducting conflict resolution.
Consider this yet another plug to consider immutability.