Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
761 additions
and
145 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package net.spy.memcached; | ||
|
||
/** | ||
* Defines a mutation mechanism for a high-level CAS client interface. | ||
*/ | ||
public interface CASMutation<T> { | ||
|
||
/** | ||
* Get the new value to replace the current value. | ||
* | ||
* @param current the current value in the cache | ||
* @return the replacement value | ||
*/ | ||
T getNewValue(T current); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package net.spy.memcached; | ||
|
||
import net.spy.SpyObject; | ||
|
||
/** | ||
* Object that provides mutation via CAS over a given memcache client. | ||
* | ||
* <p>Example usage (reinventing incr):</p> | ||
* | ||
* <pre> | ||
* // Get or create a client. | ||
* MemcachedClient client=[...]; | ||
* | ||
* // Get a mutator instance that uses that client. | ||
* CASMutator<Long> mutator=new CASMutator<Long>(client); | ||
* | ||
* // 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); | ||
* </pre> | ||
*/ | ||
public class CASMutator<T> extends SpyObject { | ||
|
||
private final MemcachedClient client; | ||
|
||
/** | ||
* Construct a CASMutator that uses the given client. | ||
* | ||
* @param c the client | ||
*/ | ||
public CASMutator(MemcachedClient c) { | ||
super(); | ||
client=c; | ||
} | ||
|
||
/** | ||
* CAS a new value in for a key. | ||
* | ||
* @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, long initialExp, | ||
final CASMutation<T> m) throws Exception { | ||
T rv=initial; | ||
|
||
boolean done=false; | ||
while(!done) { | ||
CASValue casval=client.gets(key); | ||
T current=null; | ||
// If there were a CAS value, check to see if it's compatible. | ||
if(casval != null) { | ||
@SuppressWarnings("unchecked") | ||
T tmp = (T)casval.getValue(); | ||
current=tmp; | ||
} | ||
// If we have anything mutate and CAS, else add. | ||
if(current != null) { | ||
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) == CASResponse.OK) { | ||
done=true; | ||
} | ||
} else { | ||
// No value found, try an add. | ||
if(client.add(key, 0, initial).get()) { | ||
done=true; | ||
rv=initial; | ||
} | ||
} | ||
} | ||
|
||
return rv; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package net.spy.memcached; | ||
|
||
/** | ||
* Response codes for a CAS operation. | ||
*/ | ||
public enum CASResponse { | ||
/** | ||
* Status indicating that the CAS was successful and the new value is | ||
* stored in the cache. | ||
*/ | ||
OK, | ||
/** | ||
* Status indicating the value was not found in the cache (an add | ||
* operation may be issued to store the value). | ||
*/ | ||
NOT_FOUND, | ||
/** | ||
* Status indicating the value was found in the cache, but exists with a | ||
* different CAS value than expected. In this case, the value must be | ||
* refetched and the CAS operation tried again. | ||
*/ | ||
EXISTS | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package net.spy.memcached; | ||
|
||
/** | ||
* A value with a CAS identifier. | ||
*/ | ||
public class CASValue { | ||
private final long cas; | ||
private final Object value; | ||
|
||
/** | ||
* Construct a new CASValue with the given identifer and value. | ||
* | ||
* @param c the CAS identifier | ||
* @param v the value | ||
*/ | ||
public CASValue(long c, Object v) { | ||
super(); | ||
cas=c; | ||
value=v; | ||
} | ||
|
||
/** | ||
* Get the CAS identifier. | ||
*/ | ||
public long getCas() { | ||
return cas; | ||
} | ||
|
||
/** | ||
* Get the object value. | ||
*/ | ||
public Object getValue() { | ||
return value; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package net.spy.memcached.ops; | ||
|
||
/** | ||
* Operation that represents compare-and-swap. | ||
*/ | ||
public interface CASOperation extends Operation { | ||
// nothing | ||
} |
24 changes: 24 additions & 0 deletions
24
src/main/java/net/spy/memcached/ops/CASOperationStatus.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package net.spy.memcached.ops; | ||
|
||
import net.spy.memcached.CASResponse; | ||
|
||
/** | ||
* OperationStatus subclass for indicating CAS status. | ||
*/ | ||
public class CASOperationStatus extends OperationStatus { | ||
|
||
private final CASResponse casResponse; | ||
|
||
public CASOperationStatus(boolean success, String msg, CASResponse cres) { | ||
super(success, msg); | ||
casResponse=cres; | ||
} | ||
|
||
/** | ||
* Get the CAS response indicated here. | ||
*/ | ||
public CASResponse getCASResponse() { | ||
return casResponse; | ||
} | ||
|
||
} |
12 changes: 12 additions & 0 deletions
12
src/main/java/net/spy/memcached/ops/CancelledOperationStatus.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package net.spy.memcached.ops; | ||
|
||
/** | ||
* Operation status indicating an operation was cancelled. | ||
*/ | ||
public class CancelledOperationStatus extends OperationStatus { | ||
|
||
public CancelledOperationStatus() { | ||
super(false, "cancelled"); | ||
} | ||
|
||
} |
Oops, something went wrong.