+ propagate session changes to RedisAtomicLong as well

This commit is contained in:
Costin Leau
2011-01-27 18:12:03 +02:00
parent ec7f003dff
commit d24aaa0820
2 changed files with 100 additions and 46 deletions

View File

@@ -17,9 +17,13 @@ package org.springframework.data.keyvalue.redis.support.atomic;
import java.io.Serializable;
import java.util.Collections;
import java.util.concurrent.Callable;
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
import org.springframework.data.keyvalue.redis.core.KeyBound;
import org.springframework.data.keyvalue.redis.core.RedisOperations;
import org.springframework.data.keyvalue.redis.core.RedisTemplate;
import org.springframework.data.keyvalue.redis.core.SessionCallback;
import org.springframework.data.keyvalue.redis.core.ValueOperations;
/**
@@ -35,6 +39,41 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
private ValueOperations<String, Long> operations;
private RedisOperations<String, Long> generalOps;
/**
* Constructs a new <code>RedisAtomicLong</code> instance.
*
* @param redisCounter redis counter
* @param factory connection factory
*/
public RedisAtomicLong(String redisCounter, RedisConnectionFactory factory) {
RedisTemplate<String, Long> redisTemplate = new RedisTemplate<String, Long>(factory);
redisTemplate.setExposeConnection(true);
this.key = redisCounter;
this.generalOps = redisTemplate;
this.operations = generalOps.opsForValue();
if (this.operations.get(redisCounter) == null) {
set(0);
}
}
/**
* Constructs a new <code>RedisAtomicLong</code> instance.
*
* @param redisCounter
* @param factory
* @param initialValue
*/
public RedisAtomicLong(String redisCounter, RedisConnectionFactory factory, long initialValue) {
RedisTemplate<String, Long> redisTemplate = new RedisTemplate<String, Long>(factory);
redisTemplate.setExposeConnection(true);
this.key = redisCounter;
this.generalOps = redisTemplate;
this.operations = generalOps.opsForValue();
this.operations.set(redisCounter, initialValue);
}
/**
* Constructs a new <code>RedisAtomicLong</code> instance. Uses as initial value
* the data from the backing store (sets the counter to 0 if no value is found).
@@ -109,20 +148,26 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public boolean compareAndSet(long expect, long update) {
for (;;) {
generalOps.watch(Collections.singleton(key));
if (expect == get()) {
generalOps.multi();
set(update);
if (generalOps.exec() != null) {
return true;
public boolean compareAndSet(final long expect, final long update) {
return generalOps.execute(new SessionCallback<Boolean>() {
@Override
public Boolean execute(RedisOperations operations) {
for (;;) {
operations.watch(Collections.singleton(key));
if (expect == get()) {
generalOps.multi();
set(update);
if (operations.exec() != null) {
return true;
}
}
{
return false;
}
}
}
else {
return false;
}
}
});
}
/**
@@ -131,15 +176,15 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
* @return the previous value
*/
public long getAndIncrement() {
for (;;) {
generalOps.watch(Collections.singleton(key));
long value = get();
generalOps.multi();
operations.increment(key, 1);
if (generalOps.exec() != null) {
return CASUtils.execute(generalOps, key, new Callable<Long>() {
@Override
public Long call() throws Exception {
long value = get();
generalOps.multi();
operations.increment(key, 1);
return value;
}
}
});
}
/**
@@ -148,15 +193,15 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
* @return the previous value
*/
public long getAndDecrement() {
for (;;) {
generalOps.watch(Collections.singleton(key));
long value = get();
generalOps.multi();
operations.increment(key, -1);
if (generalOps.exec() != null) {
return CASUtils.execute(generalOps, key, new Callable<Long>() {
@Override
public Long call() throws Exception {
long value = get();
generalOps.multi();
operations.increment(key, -11);
return value;
}
}
});
}
/**
@@ -165,16 +210,16 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
* @param delta the value to add
* @return the previous value
*/
public long getAndAdd(long delta) {
for (;;) {
generalOps.watch(Collections.singleton(key));
long value = get();
generalOps.multi();
set(value + delta);
if (generalOps.exec() != null) {
public long getAndAdd(final long delta) {
return CASUtils.execute(generalOps, key, new Callable<Long>() {
@Override
public Long call() throws Exception {
long value = get();
generalOps.multi();
set(value + delta);
return value;
}
}
});
}
/**
@@ -202,8 +247,7 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
* @return the updated value
*/
public long addAndGet(long delta) {
// TODO: is this really safe
return operations.increment(key, (int) delta);
return operations.increment(key, delta);
}
/**

View File

@@ -33,14 +33,16 @@ import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory
* @author Costin Leau
*/
@RunWith(Parameterized.class)
public class RedisAtomicIntegerTest {
public class RedisAtomicTests {
private RedisAtomicInteger counter;
private RedisAtomicInteger intCounter;
private RedisAtomicLong longCounter;
private RedisConnectionFactory factory;
public RedisAtomicIntegerTest(RedisConnectionFactory factory) {
counter = new RedisAtomicInteger(getClass().getSimpleName(), factory);
public RedisAtomicTests(RedisConnectionFactory factory) {
intCounter = new RedisAtomicInteger(getClass().getSimpleName() + ":int", factory);
longCounter = new RedisAtomicLong(getClass().getSimpleName() + ":long", factory);
this.factory = factory;
}
@@ -62,10 +64,18 @@ public class RedisAtomicIntegerTest {
}
@Test
public void testCheckAndSet() throws Exception {
counter.set(0);
assertFalse(counter.compareAndSet(1, 10));
assertTrue(counter.compareAndSet(0, 10));
assertTrue(counter.compareAndSet(10, 0));
public void testIntCheckAndSet() throws Exception {
intCounter.set(0);
assertFalse(intCounter.compareAndSet(1, 10));
assertTrue(intCounter.compareAndSet(0, 10));
assertTrue(intCounter.compareAndSet(10, 0));
}
}
@Test
public void testLongCheckAndSet() throws Exception {
longCounter.set(0);
assertFalse(longCounter.compareAndSet(1, 10));
assertTrue(longCounter.compareAndSet(0, 10));
assertTrue(longCounter.compareAndSet(10, 0));
}
}