Skip to content

Memcached - Get CAS Data

CAS (Check-And-Set) is a powerful Memcached feature that allows you to perform atomic read-modify-write operations. It prevents race conditions where multiple clients might try to update the same key simultaneously, potentially overwriting each other’s changes. The gets command is the first step in a CAS operation; it retrieves an item along with a unique 64-bit ‘CAS token’.

The syntax is the same as get, but the server’s response includes an extra value: the CAS token.

gets key
# First, set a value to work with
set user:profile 0 900 20
{"name":"Alice","visits":10}
STORED
# Now, use gets to retrieve the value and its CAS token
gets user:profile
VALUE user:profile 0 20 52
{"name":"Alice","visits":10}
END

In the VALUE line, the number 52 is the unique CAS token for the current state of this item. This token must be sent back with the cas command to successfully update the item. If another client modifies the item first, the CAS token will change, and your update will fail.

The complete Check-And-Set workflow involves three steps: 1) Use gets to retrieve the value and its token. 2) Modify the value in your application code. 3) Use cas to write the new value back, providing the original token. The update will only succeed if the token on the server still matches the one you provided.

import net.spy.memcached.CASValue;
import net.spy.memcached.CASResponse;
import net.spy.memcached.MemcachedClient;
import java.net.InetSocketAddress;
public class CasExample {
public static void main(String[] args) {
MemcachedClient mcc = null;
try {
mcc = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211));
System.out.println("Connection to server successful.");
// 1. Initialize a value we want to modify atomically
String key = "user:settings";
mcc.set(key, 900, "theme:dark").get(); // .get() waits for completion
System.out.println("Initial value set to: 'theme:dark'");
// --- START of the CAS operation ---
// 2. Get the value and its CAS token
CASValue<Object> casValue = mcc.gets(key);
if (casValue == null) {
System.err.println("Cache miss. Could not find key: " + key);
return; // Exit if the key doesn't exist
}
System.out.println("Retrieved value: '" + casValue.getValue() + "' with CAS token: " + casValue.getCas());
// 3. Modify the value in our application logic
String newValue = "theme:light";
// 4. Attempt to update using the cas() command
// The cas method takes: key, original CAS token, expiration, and the new value.
CASResponse response = mcc.cas(key, casValue.getCas(), 900, newValue);
System.out.println("Attempting CAS update with new value '" + newValue + "'...");
System.out.println("CAS Operation Result: " + response);
if (response == CASResponse.OK) {
System.out.println("Update successful!");
} else {
System.out.println("Update failed. The value was likely modified by another client.");
}
// To prove it, let's get the final value
System.out.println("Final value in cache: " + mcc.get(key));
} catch (Exception e) {
System.err.println("An error occurred: " + e.getMessage());
} finally {
if (mcc != null) {
mcc.shutdown();
}
}
}
}
Connection to server successful.
Initial value set to: 'theme:dark'
Retrieved value: 'theme:dark' with CAS token: 53
Attempting CAS update with new value 'theme:light'...
CAS Operation Result: OK
Update successful!
Final value in cache: theme:light

When a cas operation returns EXISTS (indicating the data was changed by another process) or NOT_FOUND (indicating the data was deleted), your application should not give up. The standard practice is to implement a retry loop. This loop will re-run the entire gets -> modify -> cas cycle until it either succeeds or a maximum number of retries is reached to prevent an infinite loop.