net.spy.memcached.CASMutator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spymemcached Show documentation
Show all versions of spymemcached Show documentation
A client library for memcached.
/**
* Copyright (C) 2006-2009 Dustin Sallings
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
* IN THE SOFTWARE.
*/
package net.spy.memcached;
import net.spy.memcached.compat.SpyObject;
import net.spy.memcached.transcoders.Transcoder;
/**
* Object that provides mutation via CAS over a given memcache client.
*
*
* Example usage (reinventing incr):
*
*
*
* // Get or create a client.
* MemcachedClient client=[...];
*
* // Get a Transcoder.
* Transcoder tc = new LongTranscoder();
*
* // Get a mutator instance that uses that client.
* CASMutator<Long> mutator=new CASMutator<Long>(client, tc);
*
* // Get a mutation that knows what to do when a value is found.
* CASMutation<Long> mutation=new CASMutation<Long>() {
* public Long getNewValue(Long current) {
* return current + 1;
* }
* };
*
* // Do a mutation.
* long currentValue=mutator.cas(someKey, 0L, 0, mutation);
*
*/
public class CASMutator extends SpyObject {
private static final int MAX_TRIES = 8192;
private final MemcachedClientIF client;
private final Transcoder transcoder;
private final int max;
/**
* Construct a CASMutator that uses the given client.
*
* @param c the client
* @param tc the Transcoder to use
* @param maxTries the maximum number of attempts to get a CAS to succeed
*/
public CASMutator(MemcachedClientIF c, Transcoder tc, int maxTries) {
super();
client = c;
transcoder = tc;
max = maxTries;
}
/**
* Construct a CASMutator that uses the given client.
*
* @param c the client
* @param tc the Transcoder to use
*/
public CASMutator(MemcachedClientIF c, Transcoder tc) {
this(c, tc, MAX_TRIES);
}
/**
* CAS a new value in for a key.
*
*
* Note that if initial is null, this method will only update existing values.
*
*
* @param key the key to be CASed
* @param initial the value to use when the object is not cached
* @param initialExp the expiration time to use when initializing
* @param m the mutation to perform on an object if a value exists for the key
* @return the new value that was set
*/
public T cas(final String key, final T initial, int initialExp,
final CASMutation m) throws Exception {
T rv = initial;
boolean done = false;
for (int i = 0; !done && i < max; i++) {
CASValue casval = client.gets(key, transcoder);
T current = null;
// If there were a CAS value, check to see if it's compatible.
if (casval != null) {
T tmp = casval.getValue();
current = tmp;
}
// If we have anything mutate and CAS, else add.
if (current != null) {
// Declaring this impossible since the only way current can
// be non-null is if casval was set.
assert casval != null : "casval was null with a current value";
rv = m.getNewValue(current);
// There are three possibilities here:
// 1) It worked and we're done.
// 2) It collided and we need to reload and try again.
// 3) It disappeared between our fetch and our cas.
// We're ignoring #3 because it's *extremely* unlikely and the
// behavior will be fine in this code -- we'll do another gets
// and follow it up with either an add or another cas depending
// on whether it exists the next time.
if (client.cas(key, casval.getCas(), initialExp, rv, transcoder)
== CASResponse.OK) {
done = true;
}
} else {
// No value found, try an add.
if (initial == null) {
done = true;
rv = null;
} else if (client.add(key, initialExp, initial, transcoder).get()) {
done = true;
rv = initial;
}
}
}
if (!done) {
throw new RuntimeException("Couldn't get a CAS in " + max + " attempts");
}
return rv;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy