All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.spy.memcached.CASMutator Maven / Gradle / Ivy

The newest version!
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 max_tries the maximum number of attempts to get a CAS to succeed */ public CASMutator(MemcachedClientIF c, Transcoder tc, int max_tries) { super(); client=c; transcoder=tc; max=max_tries; } /** * 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 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(), 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 - 2025 Weber Informatics LLC | Privacy Policy