Reputation: 2893
I am using spymemcached client to implement my caching logic. Somehow I need to use CAS for concurrently modifying some content in the cache.
I saw the owner has already showed a very good example of how to use CASMutation from here: http://code.google.com/p/spymemcached/wiki/Examples#Using_CAS
But I have one question about this part of the code:
// Not strictly necessary if you specify the storage as
// LinkedList (our initial value isn't), but I like to keep
// things functional anyway, so I'm going to copy this list
// first.
LinkedList<Item> ll = new LinkedList<Item>(current);
Even though I carefully read the comments, I still don't understand exactly what it is trying to do here. What if we just use "current" without copying to "ll"? What are the potential issues?
[UPDATE]
I am following the example code and implemented a method like this, Will that work?
public <T> Set<T> addItemToSet(String key, int expire, final T newItem) throws Exception {
// This is how we modify a list when we find one in the cache.
CASMutation<Set<T>> mutation = new CASMutation<Set<T>>() {
// This is only invoked when a value actually exists.
public Set<T> getNewValue(Set<T> current) {
current.add( newItem );
return current;
}
};
HashSet<T> initialValue= new HashSet<T>();
initialValue.add( newItem );
CASMutator<Set<T>> mutator = new CASMutator<Set<T>>( memClient, getTranscoder() );
return mutator.cas(key, initialValue, expire, mutation);
}
I am mostly concerned whether it is thread-safe.
Upvotes: 0
Views: 1537
Reputation: 103807
Without having used the library, I believe that what would happen if you didn't copy, in this particular case, is that you'd get an exception as you try to modify Collections.singletonList()
, which is immutable. Some implementations of List
simply can't be modified, and it's generally a bad idea to assume you can. (It would be great if Java separated the concept of a List
as an indexed readable sequence, and a MutableList
subinterface with all of the add
and set
operations, but oh well).
In a broader sense I'm not even sure it's "not strictly necessary". What's happening here is that you're being given the existing list of items, and are asked to return a new value to be used if the CAS conditions hold. If you modify the list in place and then return current
, the issue is that you've lost conditionality - the current list has been modified (by you), even if the CAS logic means that your proposed new value gets rejected.
Now maybe this doesn't actually matter in practice because the library logic means that the value actually stored in the cache isn't updated; and changing the transient state isn't a problem either, because after enough attempts your update will succeed.
But with all that, I agree that there's no reason at all not to use the functional approach - you're being given an input argument to read, and are expected to return a list containing your modifications. It's much better for clarity, robustness and reasoning about the program, to create a new list with the correct values and return that. Doing otherwise may or may not work, but who wants to write code whose semantics depend on implementation details of other libraries, when you have the option of writing something unambiguous?
Upvotes: 2