Search:

Search all manuals
Search this manual
Manual
Membase and Java Tutorial
Additional Resources
Community Wiki
Community Forums
Couchbase SDKs
Parent Section
Membase and Java Tutorial
Chapter Sections
Chapters

10. Error Handling

At this point, you may still be wondering how CAS values are used to prevent clients from writing over values that were changed by another client. Here is the answer:

In essence, the CAS value exists so that that a client can 'hold' the CAS value for a item ID that it knows, and only update the item if the CAS has not changed. Hence, Compare And Swap (CAS). In a multi-client environment it's designed to prevent one client changing the value of an item when another client may have already updated it.

Unfortunately there's no way to lock items; individual operations (set, for example) are atomic, but multiple operations are not, and this is what CAS is designed to protect against. To stop you changing a value that has changed since the last GET.

In order to demonstrate this situation, add the bold lines to the processInput method to allow a way to perform a CAS operation and see what happens if two copies of the program are run at the same time:

} else if (input.startsWith("/who")) {
                System.out.println("Users connected: "
                        + client.get("CurrentUsers"));
            } else if (input.startsWith("/cas")) {
                runCasTest();
            } else {

Now create the runCasTest() method at the bottom of the class:

private void runCasTest() {

        System.out.println("Testing a CAS operation.");
        CASValue<Object> cas = client.gets("CasTest");

        if (cas == null) {
            // Must create it first
            System.out.println("Creating CasTest value.");
            client.set("CasTest", 120, "InitialValue");
            return;
        }

        System.out.println("CAS for CasTest = "+cas.getCas());
        System.out.println("Sleeping for 10 seconds.");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
        }

        CASResponse response =
            client.cas("CasTest", cas.getCas(), "ReplacedValue");
        if (response.equals(CASResponse.OK)) {
            System.out.println("OK response.");
        }
        else if (response.equals(CASResponse.EXISTS)) {
            System.out.println("EXISTS response.");
        }
        else if (response.equals(CASResponse.NOT_FOUND)) {
            System.out.println("NOT_FOUND response.");
        }

        cas = client.gets("CasTest");
        System.err.println("CAS after = "+cas.getCas());
    }

The first time the test is run (by typing "/cas" while the application is running) the gets() method will return null, so it will just set the CasTest key to "InitialValue" and return. The second time the test is run it will get a CASValue<Object> instance from the gets() method, print out its value, and then sleep for 10 seconds. Then after sleeping, the code performs a client.cas() method call to replace the value.

If you run this in two different windows you may see output something like the following if you time things just right:

/cas
Testing a CAS operation.
Creating CasTest value.
/cas
Testing a CAS operation.
CAS for CasTest = 74
Sleeping for 10 seconds.
OK response.
CAS after = 75
/cas
Testing a CAS operation.
CAS for CasTest = 75
Sleeping for 10 seconds.
EXISTS response.
CAS after = 79

In the second copy of the application, the output would look something like this:

/cas
Testing a CAS operation.
CAS for CasTest = 75
Sleeping for 10 seconds.
OK response.
CAS after = 79

What you see is that when the CAS is 75, the second client modifies the value before the first client is done sleeping. Instead of getting an OK response, that client gets an EXISTS response indicating that a change has been made before it had a chance to do so, so its client.cas() operation failed, and the code would have to handle this situation to rectify the situation.

Locking is not an option if you want to have an operational distributed database. Locking causes contention and complexity. Being able to quickly detect when a value has been overwritten without having to hold a lock is a simple solution to the problem. You will be able to write code to handle the situation either by returning an error to the user, or retrying the operation after obtaining another CAS value.