Add Redis 2.6 dump and restore commands to Connections

DATAREDIS-182
This commit is contained in:
Jennifer Hickey
2013-06-16 13:20:52 -07:00
parent 5817e24b9e
commit ff60d0d290
18 changed files with 437 additions and 48 deletions

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis;
/**
*
* @author Jennifer Hickey
*
*/
abstract public class SpinBarrier {
/**
* Periodically tests for a condition until it is met or a timeout occurs
*
* @param condition
* The condition to periodically test
* @param timeout
* The timeout
* @return true if condition passes, false if condition does not pass within
* timeout
*/
public static boolean waitFor(TestCondition condition, long timeout) {
boolean passes = false;
for (long currentTime = System.currentTimeMillis(); System.currentTimeMillis() - currentTime < timeout;) {
if (condition.passes()) {
passes = true;
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
return passes;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis;
/**
*
* A condition to test periodically, used in conjunction with
* {@link SpinBarrier}
*
* @author Jennifer Hickey
*
*/
public interface TestCondition {
/**
*
* @return true if condition passes
*/
boolean passes();
}

View File

@@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.springframework.data.redis.SpinBarrier.waitFor;
import java.util.ArrayList;
import java.util.Arrays;
@@ -44,7 +45,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.Address;
import org.springframework.data.redis.Person;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.RedisTestProfileValueSource;
import org.springframework.data.redis.TestCondition;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
@@ -562,6 +565,53 @@ public abstract class AbstractConnectionIntegrationTests {
assertTrue(connection.pTtl("whatup") > -1);
}
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testDumpAndRestore() {
connection.set("testing", "12");
byte[] serializedResults = connection.dump("testing".getBytes());
assertNotNull(serializedResults);
connection.del("testing");
assertNull(connection.get("testing"));
connection.restore("testing".getBytes(), 0, serializedResults);
assertEquals("12",connection.get("testing"));
}
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testDumpNonExistentKey() {
actual.add(connection.dump("fakey".getBytes()));
verifyResults(Arrays.asList(new Object[] { null }), actual);
}
@Test(expected=RedisSystemException.class)
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreBadData() {
// Use something other than dump-specific serialization
connection.restore("testing".getBytes(), 0, "foo".getBytes());
}
@Test(expected=RedisSystemException.class)
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreExistingKey() {
connection.set("testing", "12");
byte[] serializedResults = connection.dump("testing".getBytes());
assertNotNull(serializedResults);
connection.restore("testing".getBytes(), 0, serializedResults);
}
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreTtl() {
connection.set("testing", "12");
byte[] serializedResults = connection.dump("testing".getBytes());
assertNotNull(serializedResults);
connection.del("testing");
assertNull(connection.get("testing"));
connection.restore("testing".getBytes(), 100l, serializedResults);
assertTrue(waitFor(new KeyExpired("testing"), 300l));
}
@Test
public void testType() {
connection.set("something", "yo");
@@ -1257,26 +1307,6 @@ public abstract class AbstractConnectionIntegrationTests {
assertEquals(expected, actual);
}
protected boolean waitFor(TestCondition condition, long timeout) {
boolean passes = false;
for (long currentTime = System.currentTimeMillis(); System.currentTimeMillis()
- currentTime < timeout;) {
if (condition.passes()) {
passes = true;
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
return passes;
}
protected interface TestCondition {
public boolean passes();
}
protected class KeyExpired implements TestCondition {
private String key;

View File

@@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.springframework.data.redis.SpinBarrier.waitFor;
import java.util.ArrayList;
import java.util.Arrays;
@@ -35,6 +37,7 @@ import java.util.UUID;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.data.redis.TestCondition;
import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
import org.springframework.data.redis.connection.StringRedisConnection.StringTuple;
@@ -160,6 +163,57 @@ abstract public class AbstractConnectionPipelineIntegrationTests extends
}, 1000l));
}
@Test(expected=RedisPipelineException.class)
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreBadData() {
// Use something other than dump-specific serialization
connection.restore("testing".getBytes(), 0, "foo".getBytes());
getResults();
}
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreExistingKey() {
connection.set("testing", "12");
connection.dump("testing".getBytes());
List<Object> results = getResults();
initConnection();
connection.restore("testing".getBytes(), 0, (byte[]) results.get(1));
try {
getResults();
fail("Expected pipeline exception restoring an existing key");
}catch(RedisPipelineException e) {
}
}
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreTtl() {
connection.set("testing", "12");
connection.dump("testing".getBytes());
List<Object> results = getResults();
initConnection();
actual.add(connection.del("testing"));
actual.add(connection.get("testing"));
connection.restore("testing".getBytes(), 100l, (byte[]) results.get(results.size() - 1));
verifyResults(Arrays.asList(new Object[] { 1l, null }), actual);
assertTrue(waitFor(new KeyExpired("testing"), 300l));
}
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testDumpAndRestore() {
connection.set("testing", "12");
connection.dump("testing".getBytes());
List<Object> results = getResults();
initConnection();
actual.add(connection.del("testing"));
actual.add((connection.get("testing")));
connection.restore("testing".getBytes(), 0, (byte[]) results.get(results.size() - 1));
actual.add(connection.get("testing"));
verifyResults(Arrays.asList(new Object[] { 1l, null, "12" }), actual);
}
@Test
@IfProfileValue(name = "runLongTests", value = "true")
public void testExpire() throws Exception {

View File

@@ -80,6 +80,31 @@ public class JedisConnectionIntegrationTests extends AbstractConnectionIntegrati
super.testPTtlNoExpire();
}
@Test(expected=UnsupportedOperationException.class)
public void testDumpAndRestore() {
super.testDumpAndRestore();
}
@Test(expected=UnsupportedOperationException.class)
public void testDumpNonExistentKey() {
super.testDumpNonExistentKey();
}
@Test(expected=UnsupportedOperationException.class)
public void testRestoreBadData() {
super.testRestoreBadData();
}
@Test(expected=UnsupportedOperationException.class)
public void testRestoreExistingKey() {
super.testRestoreExistingKey();
}
@Test(expected=UnsupportedOperationException.class)
public void testRestoreTtl() {
super.testRestoreTtl();
}
@Test
public void testIncrDecrByLong() {
String key = "test.count";

View File

@@ -122,6 +122,31 @@ public class JedisConnectionPipelineIntegrationTests extends
super.testPTtlNoExpire();
}
@Test(expected=UnsupportedOperationException.class)
public void testDumpAndRestore() {
super.testDumpAndRestore();
}
@Test(expected=UnsupportedOperationException.class)
public void testDumpNonExistentKey() {
super.testDumpNonExistentKey();
}
@Test(expected=UnsupportedOperationException.class)
public void testRestoreBadData() {
super.testRestoreBadData();
}
@Test(expected=UnsupportedOperationException.class)
public void testRestoreExistingKey() {
super.testRestoreExistingKey();
}
@Test(expected=UnsupportedOperationException.class)
public void testRestoreTtl() {
super.testRestoreTtl();
}
@Test(expected = RedisSystemException.class)
public void testBitSet() throws Exception {
super.testBitSet();

View File

@@ -318,6 +318,31 @@ public class JRedisConnectionIntegrationTests extends AbstractConnectionIntegrat
super.testPTtlNoExpire();
}
@Test(expected=UnsupportedOperationException.class)
public void testDumpAndRestore() {
super.testDumpAndRestore();
}
@Test(expected=UnsupportedOperationException.class)
public void testDumpNonExistentKey() {
super.testDumpNonExistentKey();
}
@Test(expected=UnsupportedOperationException.class)
public void testRestoreBadData() {
super.testRestoreBadData();
}
@Test(expected=UnsupportedOperationException.class)
public void testRestoreExistingKey() {
super.testRestoreExistingKey();
}
@Test(expected=UnsupportedOperationException.class)
public void testRestoreTtl() {
super.testRestoreTtl();
}
// Jredis returns null for rPush
@Test
public void testSort() {

View File

@@ -27,6 +27,7 @@ import org.junit.runner.RunWith;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.connection.AbstractConnectionIntegrationTests;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.test.annotation.IfProfileValue;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -42,6 +43,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
public class LettuceConnectionIntegrationTests extends AbstractConnectionIntegrationTests {
@Test
@IfProfileValue(name = "runLongTests", value = "true")
public void testMultiThreadsOneBlocking() throws Exception {
Thread th = new Thread(new Runnable() {
public void run() {

View File

@@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.data.redis.SpinBarrier.waitFor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -32,6 +32,7 @@ import java.util.UUID;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.redis.TestCondition;
import org.springframework.data.redis.connection.AbstractConnectionPipelineIntegrationTests;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.DefaultStringTuple;

View File

@@ -1,14 +1,17 @@
package org.springframework.data.redis.connection.lettuce;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.junit.Test;
import org.springframework.data.redis.connection.RedisPipelineException;
import org.springframework.test.annotation.IfProfileValue;
/**
* Integration test of {@link LettuceConnection} transactions within a pipeline
@@ -30,8 +33,42 @@ public class LettuceConnectionPipelineTxIntegrationTests extends
connection.set("dbparam", "foo");
assertNull(connection.dbSize());
List<Object> results = getResults();
assertEquals(1, results.size());
assertTrue((Long) results.get(0) > 0);
assertEquals(3, results.size());
assertTrue((Long) results.get(2) > 0);
}
@Test
public void testInfo() throws Exception {
assertNull(connection.info());
List<Object> results = getResults();
assertEquals(2, results.size());
Properties info = LettuceUtils.info((String) results.get(1));
assertTrue("at least 5 settings should be present", info.size() >= 5);
String version = info.getProperty("redis_version");
assertNotNull(version);
}
@Test(expected=RedisPipelineException.class)
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreBadData() {
// Use something other than dump-specific serialization
connection.restore("testing".getBytes(), 0, "foo".getBytes());
getResults();
}
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreExistingKey() {
connection.set("testing", "12");
connection.dump("testing".getBytes());
List<Object> results = getResults();
initConnection();
connection.restore("testing".getBytes(), 0, (byte[]) results.get(2));
try {
getResults();
fail("Expected RedisPipelineException restoring existing key");
}catch(RedisPipelineException e) {
}
}
protected void initConnection() {
@@ -41,15 +78,7 @@ public class LettuceConnectionPipelineTxIntegrationTests extends
protected List<Object> getResults() {
assertNull(connection.exec());
List<Object> results = new ArrayList<Object>();
List<Object> pipelinedResults = connection.closePipeline();
// filter out the return value of exec
for (Object result : pipelinedResults) {
if (!"OK".equals(result) && !("QUEUED").equals(result)) {
results.add(result);
}
}
return results;
return connection.closePipeline();
}
}

View File

@@ -16,15 +16,19 @@
package org.springframework.data.redis.connection.lettuce;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.IfProfileValue;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.lambdaworks.redis.RedisException;
/**
* Integration test of {@link LettuceConnection} functionality within a
* transaction
@@ -93,6 +97,27 @@ public class LettuceConnectionTransactionIntegrationTests extends
assertNull(getResults());
}
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreBadData() {
// Use something other than dump-specific serialization
connection.restore("testing".getBytes(), 0, "foo".getBytes());
List<Object> results = getResults();
assertTrue(results.get(0) instanceof RedisException);
}
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreExistingKey() {
connection.set("testing", "12");
connection.dump("testing".getBytes());
List<Object> results = getResults();
initConnection();
connection.restore("testing".getBytes(), 0, (byte[]) results.get(1));
List<Object> restoreResults = getResults();
assertTrue(restoreResults.get(0) instanceof RedisException);
}
protected void initConnection() {
connection.multi();
}

View File

@@ -29,8 +29,7 @@ import org.springframework.test.annotation.IfProfileValue;
* @author Jennifer Hickey
*
*/
public class SrpConnectionTransactionIntegrationTests extends
SrpConnectionPipelineIntegrationTests {
public class SrpConnectionTransactionIntegrationTests extends SrpConnectionPipelineIntegrationTests {
@Ignore
public void testMultiDiscard() {
@@ -87,7 +86,7 @@ public class SrpConnectionTransactionIntegrationTests extends
}
@Test
@IfProfileValue(name = "redisVersion", values = {"2.4", "2.6"})
@IfProfileValue(name = "redisVersion", values = { "2.4", "2.6" })
public void testGetRangeSetRange() {
connection.set("rangekey", "supercalifrag");
actual.add(connection.getRange("rangekey", 0l, 2l));
@@ -96,6 +95,22 @@ public class SrpConnectionTransactionIntegrationTests extends
verifyResults(Arrays.asList(new Object[] { "sup", 13l, "suckrcalifrag" }), actual);
}
@Ignore("https://github.com/spullara/redis-protocol/issues/24")
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreBadData() {
// SRP issue exec does not report individual ErrorReplys in a MultiBulkReply
// as Exceptions
}
@Ignore("https://github.com/spullara/redis-protocol/issues/24")
@Test
@IfProfileValue(name = "redisVersion", value = "2.6")
public void testRestoreExistingKey() {
// SRP issue exec does not report individual ErrorReplys in a MultiBulkReply
// as Exceptions
}
protected void initConnection() {
connection.multi();
}