Pessimistic lock & consistency


Imagine a situation in which you have to exchange the content of two document, A and B. For example, as a real use case, this can occur in a game in which users can perform data exchange and this data is stored inside in two nodes: A contains all the data related to first user and B to the second. This operation should be “atomic”, or better, consistent.
Imagine now that application servers and Couchbase servers are physically separated and so communication can fail. Here are some scenarios related to the API:

Using pessimistic and optimistic lock together
As an example of pseudo-code:

// variables are keyA, keyB, oldValueA, oldValueB, casA, casB
if( lock( keyA, casA ) ){
if( update( keyB, oldValueA, casB ) ){
if( update( keyA, oldValueB, casA ) ){ // Automatically the lock is released
// Everything is OK, consistency OK
// INCONSISTENCY: here you can try to restore the node B to achieve consistency
// In the example given this might a situation in which user A and B have the same data
// (oldValueB) and the user A “real” data can be lost.
newCasB = getCas( keyB );
if( update( keyB, oldValueB, newCasB ) ){
// Nothing done, but consistency
// INCONSISTENCY: what can you do now?
// Is there a way to prevent this situation? Imagine connection is down.
// In traditional DBMS all the update process was inside a TRANSACTION,
// even if the DB server goes down right now, the consistency is guaranteed
// at the next boot.
// B failed, so unlock node A
if( unlock( keyA ) ){
// Nothing done, but consistency
// Nothing done, but consistency. Lock will be released by Couchbase
// Nothing done, but consistency

If you do replace with CAS you assure node consistency, but not the whole exchange operation.

Using replaceMulti
Then, I looked at node.js APIs and noticed replaceMulti that allows you to replace with CAS multiple documents. It seems that all the operations are executed independently, so if you try:

// variables are keyA, keyB, oldValueA, oldValueB, casA, casB
var kv = {keyA: { value: oldValueB, cas: casA }, keyB: {value: oldValueA, cas: casB}};
couchbase.replaceMulti( kv, {spooled: true}, function(errors, results){
if( errors ){
// Even if I am using the spooled option, it seems that
// operations are done singularly, so this is the same situation:
// INCONSISTENCY: do I have to try to restore errors independently?
// What if it fails?
// Everything is OK
} );

I haven’t found any solution to overcome this problem, can you help me?

Matteo Spampani


This is the solution: