This operation is also known as a check-and-set method; it enables you to update information only if a unique identifier matches the identifier for the document you want to change. This identifier, called a CAS value or ID, prevents an application from updating values in the database that may have changed since the application originally obtained the value.
A check-and-set operation will only allow the user with the latest CAS value to update a key. This assures you that if you get a key, and someone has changed it in the meantime, you can not change the value. Essentially the first process that accessed the document with the most current CAS value will be able to update it. When this update occurs, Couchbase Server also updates the CAS value. All other requests at this point will be sending the old, invalid CAS values.
Providing optimistic concurrency is optional in your application. All documents you create in Couchbase Server automatically have a CAS value stored as part of metadata for the document. To use it for optimistic concurrency, you include get-with-cas and check-and-set operations in your application logic as well as provide CAS values as parameters to these methods.
CAS values are in the form of 64-bit integers and they are updated every time a value is modified; if an application attempts to set or update a key/value pair and the CAS provided does not match, Couchbase Server will return an error.
For instance, imagine we want to have a repair station for our spaceship game. Players who suffer damage to their ships must go there occasionally or they cannot travel or defend themselves. However, we do not want the repair station to have an unlimited supply of spaceship replacement parts on inventory. In this example, we have a document to represent the types of spaceship parts the repair station carries, and the amounts it has in inventory.
By requiring CAS values in this scenario, we only update the inventory and provide it to a ship if we have the most current CAS value for the inventory document. If another ship has come and taken the part in the meantime, it will change the CAS value for inventory, we fail to get the part with our current CAS value and receive an error.
Typically we would perform a get-with-cas call in order to retrieve the current inventory of repair parts and the CAS value for the inventory. If the part we need is in inventory, we would use the CAS value to update our inventory document to show one less part.
By using the CAS value we will ensure that our spaceship either
gets the part given our current CAS value, or needs to check
inventory again because another ship has already taken one of
the parts. In this scenario performing get-with-cas and then a
cas call to update the inventory will
ensure that our reduction of inventory occurs in an orderly
fashion, and that spaceships can only remove inventory when they
have the right to do so by providing the correct CAS value.
Should you choose to enforce CAS values for a certain type of
key or set of application data, you should retrieve the keys and
store the CAS value returned by get-with-cas. Anytime you want
to update one of these keys, you should do so as a
cas operation.
To be able to perform a cas update you
not only need the key for a document, you will also need the CAS
value in order to successfully update it. In this case you could
also store the CAS value returned when the value was originally
created and then perform a cas
operation. In most cases however, you would find it easiest to
use get-with-cas to retrieve the CAS for a given key, and then
perform your check-and-set. In .Net, the method that retrieves a
value and CAS value for a given key is called
GetWithCas.
The following is an example of a cas operation using pseudo-code:
attempts_left = 10; loop { cas, val = Get("aKey"); new_value = updateValue(val); result = ReplaceWithCAS("aKey", new_value, cas); if (result == success) { break; # YAY, success } if (result.error == EXISTS_WITH_DIFFERENT_CAS) { --attempts_left; if (attempts_left == 0) { throw("Failed to update item 'aKey' too many times, giving up!") } continue; } throw("Unexpected error when updating item 'aKey': ", result.error); }
The first part of our loop retrieves the CAS value and value and
then changes the value. We then try to update the value in
Couchbase Server as a cas operation. If the result object sent
back by Couchbase Server is success we break, if it is a 'key
exists' error, we make additional attempts to update the value
until attempts_left is 0. At this point we
throw and exception and exit the loop.
If you perform a CAS operation and the CAS value has been
changed by another process, you will get 'key exists' error. How
you handle this error depends on the value you are trying to
update. You can try again to get the key and when you get the
value, actually compare the part you want to change with the
value you expected. It is possible that another process made an
update, but it did not update the part of the value you are
interested in changing. In this case the other process will
release the key with a cas operation.
You can then perform another get to
retrieve the new CAS value and content, then examine the
content. Here is the general sequence you could follow:
Perform a get-with-cas to retrieve the CAS value for a key,
Try a cas with the CAS value. If
you fail,
Perform a get-with-cas again to get the new CAS value, and compare the part of the value with the content you expected,
If the part of the value is still intact, try to perform
cas again with your updated content
and the new CAS value.
When you try this approach, you might want to limit the number of times you re-attempt a get-with-cas and the number of times you will try to check and update the content.
The equivalent call in the memcached protocol is
set with a CAS value provided. For more
information, see
memcached
protocol.
The only other types of errors you can typically experience with
cas are issues with the new value you
provide, such as formatting. The other error is that a key that
is truly missing, which you should have discovered when you
first performed a get-with-cas to retrieve the CAS value.
The types of errors that can occur during this operation include 1) inability to connect to a node, or 2) some error exists while attempting to format a value being stored. If you have a connection-level error you may need to reattempt connection, and possibly check the status of the server. If you have an error with the size of your value or formatting, you need to check the value itself, and how it is encoded and see if there are any issues that make the document incompatible with Couchbase Server.
For more information about connections and connection-level settings, see Section 7.5.4, “Optimizing Client Instances” and Section 7.7.1, “Client-Side Timeouts”