Data Types¶
Traditionally all data stored in Riak was an opaque binary type. Then in version 1.4 came the introduction of a counter, the first Convergent Data Type supported in Riak. In Riak 2.0, several additional Data Types were introduced. Riak “knows” about these data types, and conflicting writes to them will converge automatically without presenting sibling values to the user.
Here is the list of current Data Types:
Counterincrements or decrements integer valuesSetallows you to store multiple distinct opaque binary values against a keyMapis a nested, recursive struct, or associative array. Think of it as a container for composing ad hoc data structures from multiple Data Types. Inside a map you may store sets, counters, flags, registers, and even other mapsRegisterstores binaries accoring to last-write-wins logic withinMapFlagis similar to a boolean and also must be withinMap
All Data Types must be stored in buckets bearing a
BucketType that sets the
datatype property to one of
"counter", "set", or "map". Note that the bucket must have
the allow_mult property set to true.
These Data Types are stored just like RiakObjects, so size constraints that apply to
normal Riak values apply to Riak Data Types too.
An in-depth discussion of Data Types, also known as CRDTs, can be found at Data Types.
Examples of using Data Types can be found at Using Data Types.
Sending Operations¶
Riak Data Types provide a further departure from Riak’s usual operation, in that the API is operation-based. Rather than fetching the data structure, reconciling conflicts, mutating the result, and writing it back, you instead tell Riak what operations to perform on the Data Type. Here are some example operations:
- increment a
Counterby10- add
'joe'to aSet- remove the
Setfield called'friends'from aMap- enable the prepay
Flagin aMap
Datatypes can be fetched and created just like
RiakObject instances, using
RiakBucket.get and
RiakBucket.new, except that the
bucket must belong to a bucket-type that has a valid datatype
property. If we have a bucket-type named “social-graph” that has the
datatype “set”, we would fetch a Set like so:
graph = client.bucket_type('social-graph')
graph.datatype # => 'set'
myfollowers = graph.bucket('followers').get('seancribbs')
# => a Set datatype
Once we have a datatype, we can stage operations against it and then send those operations to Riak:
myfollowers.add('javajolt')
myfollowers.discard('roach')
myfollowers.update()
While this looks in code very similar to manipulating
RiakObject instances, only mutations are
enqueued locally, not the new value.
Context and Observed-Remove¶
In order for Riak Data Types to behave well, you must have an opaque context received from a read when you:
disableaFlag(set it tofalse)- remove a field from a
Mapremovean element from aSet
The basic rule is “you cannot remove something you haven’t seen”, and
the context tells Riak what you’ve actually seen, similar to the
Vector clock on RiakObject. The Python
client handles opaque contexts for you transparently as long as you
fetch before performing one of these actions.
Datatype abstract class¶
Map¶
-
Map.counters¶ Filters keys in the map to only those of counter types. Example:
map.counters['views'].increment() del map.counters['points']
-
Map.flags¶ Filters keys in the map to only those of flag types. Example:
map.flags['confirmed'].enable() del map.flags['attending']
-
Map.maps¶ Filters keys in the map to only those of map types. Example:
map.maps['emails'].registers['home'].set("user@example.com") del map.maps['spam']
-
Map.registers¶ Filters keys in the map to only those of register types. Example:
map.registers['username'].set_value("riak-user") del map.registers['access_key']
-
Map.sets¶ Filters keys in the map to only those of set types. Example:
map.sets['friends'].add("brett") del map.sets['favorites']
Map-only datatypes¶
Two of the new Data Types may only be embedded in
Map objects (in addition to
Map itself):