If you need to coordinate shared access on documents, Couchbase helps you with two approaches. Depending on the application you may need to use both of them, but in general it is better (if feasible) to lean towards CAS because it provides the better performance characteristics.
Optimistic Locking. Each document has a unique identifier associated with it (the CAS value), which changes when the document itself is mutated. You can fetch the CAS value for a given key and pass it to any mutator operation to protect it. The update will only succeed, when the CAS value is still the same. This is why it’s called optimistic locking. Someone else can still read and try to update the document, but it will fail once the CAS value has changed. Here is a example on how to do it with the Ruby SDK:
key = "eagle_brewing-golden" # Reads the document with the CAS value. beer, flags, cas = client.get(key, :extended => true) # Updates the document and tries to store it back. beer["name"] = "Some other Name" client.set(key, beer, :cas => cas, :flags => flags)
Note that this also means that all your application need to
follow the same code path (cooperative locking). If you use
#set somewhere else in the code on the same
document, it will work even if the CAS itself is out of date
(that’s because the normal
doesn’t care about those values at all). Of course, the
CAS itself changes then and the mutation operation would fail
There is also shortcut operation for doing optimistic locking
Bucket#cas. Internally it does the same thing
but abstract you from storing and passing meta information. Here
is the previous example rewritten to use this operation:
key = "eagle_brewing-golden" client.cas(key) do |beer| beer["name"] = "Some other Name" # return new value from block beer end
Note that you should return new value from the block. If you
will skip it, it will use
"Some other Name"
as new value.
If you want to lock a document completely (or an object
graph), you can use the
:lock option. The option
accepts either boolean (where truth does make sense really) or
Fixnum meaning the time period where the lock is valid. The
server will release lock after the that period (or maximum
value, which configured on the server). Other threads can
get queries queries against the
document, but mutation operations without a CAS will fail.
You can determine actual default and maximum values calling
Bucket#stats without arguments and inspecting
key = "eagle_brewing-golden"; # Get with Lock beer, flags, cas = client.get(key, :lock => true, :extended => true); # Update the document beer["name"] = "Some other Name" # Try to set without the lock client.set(key, beer, :flags => flags) #=> will raise Couchbase::Error::KeyExists # Try to set with the CAS aquired, will be OK client.set(key, beer, :flags => flags, :cas => cas)
Once you update the document, the lock will be released. There
is also the
Bucket#unlock method available
through which you can unlock the document.