DATAREDIS-330 - Add support for SENTINEL commands.
add support for commands: SENTINEL FAILOVER master SENTINEL SLAVES master SENTINEL REMOVE name SENTINEL MONITOR name ip port quorum Original pull request: #92.
This commit is contained in:
committed by
Thomas Darimont
parent
55ff415a71
commit
fd361a1868
@@ -0,0 +1,788 @@
|
||||
/*
|
||||
* Copyright 2014 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.connection;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.connection.RedisNode.RedisNodeBuilder;
|
||||
import org.springframework.data.redis.core.Cursor;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.data.redis.core.types.RedisClientInfo;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class RedisConnectionUnitTests {
|
||||
|
||||
private final RedisNode SENTINEL_1 = new RedisNodeBuilder().listeningAt("localhost", 23679).build();
|
||||
private AbstractDelegatingRedisConnectionStub connection;
|
||||
private RedisSentinelConnection sentinelConnectionMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
sentinelConnectionMock = mock(RedisSentinelConnection.class);
|
||||
|
||||
connection = new AbstractDelegatingRedisConnectionStub(mock(AbstractRedisConnection.class, CALLS_REAL_METHODS));
|
||||
connection.setSentinelConfiguration(new RedisSentinelConfiguration().master("mymaster").sentinel(SENTINEL_1));
|
||||
connection.setSentinelConnection(sentinelConnectionMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void shouldCloseSentinelConnectionAlongWithRedisConnection() throws IOException {
|
||||
|
||||
when(sentinelConnectionMock.isOpen()).thenReturn(true).thenReturn(false);
|
||||
|
||||
connection.setActiveNode(SENTINEL_1);
|
||||
connection.getSentinelConnection();
|
||||
connection.close();
|
||||
|
||||
verify(sentinelConnectionMock, times(1)).close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotTryToCloseSentinelConnectionsWhenAlreadyClosed() throws IOException {
|
||||
|
||||
when(sentinelConnectionMock.isOpen()).thenReturn(true);
|
||||
when(sentinelConnectionMock.isOpen()).thenReturn(false);
|
||||
|
||||
connection.setActiveNode(SENTINEL_1);
|
||||
connection.getSentinelConnection();
|
||||
connection.close();
|
||||
|
||||
verify(sentinelConnectionMock, never()).close();
|
||||
}
|
||||
|
||||
static class AbstractDelegatingRedisConnectionStub extends AbstractRedisConnection {
|
||||
|
||||
RedisConnection delegate;
|
||||
RedisNode activeNode;
|
||||
RedisSentinelConnection sentinelConnection;
|
||||
|
||||
public AbstractDelegatingRedisConnectionStub(RedisConnection delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isActive(RedisNode node) {
|
||||
return ObjectUtils.nullSafeEquals(activeNode, node);
|
||||
}
|
||||
|
||||
public void setActiveNode(RedisNode activeNode) {
|
||||
this.activeNode = activeNode;
|
||||
}
|
||||
|
||||
public void setSentinelConnection(RedisSentinelConnection sentinelConnection) {
|
||||
this.sentinelConnection = sentinelConnection;
|
||||
}
|
||||
|
||||
public boolean isSubscribed() {
|
||||
return delegate.isSubscribed();
|
||||
}
|
||||
|
||||
public void scriptFlush() {
|
||||
delegate.scriptFlush();
|
||||
}
|
||||
|
||||
public void select(int dbIndex) {
|
||||
delegate.select(dbIndex);
|
||||
}
|
||||
|
||||
public void multi() {
|
||||
delegate.multi();
|
||||
}
|
||||
|
||||
public Long rPush(byte[] key, byte[]... values) {
|
||||
return delegate.rPush(key, values);
|
||||
}
|
||||
|
||||
public byte[] get(byte[] key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
|
||||
public void scriptKill() {
|
||||
delegate.scriptKill();
|
||||
}
|
||||
|
||||
public Long sAdd(byte[] key, byte[]... values) {
|
||||
return delegate.sAdd(key, values);
|
||||
}
|
||||
|
||||
public Boolean exists(byte[] key) {
|
||||
return delegate.exists(key);
|
||||
}
|
||||
|
||||
public Subscription getSubscription() {
|
||||
return delegate.getSubscription();
|
||||
}
|
||||
|
||||
public byte[] echo(byte[] message) {
|
||||
return delegate.echo(message);
|
||||
}
|
||||
|
||||
public Boolean hSet(byte[] key, byte[] field, byte[] value) {
|
||||
return delegate.hSet(key, field, value);
|
||||
}
|
||||
|
||||
public void bgWriteAof() {
|
||||
delegate.bgWriteAof();
|
||||
}
|
||||
|
||||
public Object execute(String command, byte[]... args) {
|
||||
return delegate.execute(command, args);
|
||||
}
|
||||
|
||||
public String scriptLoad(byte[] script) {
|
||||
return delegate.scriptLoad(script);
|
||||
}
|
||||
|
||||
public byte[] getSet(byte[] key, byte[] value) {
|
||||
return delegate.getSet(key, value);
|
||||
}
|
||||
|
||||
public List<Object> exec() {
|
||||
return delegate.exec();
|
||||
}
|
||||
|
||||
public Long lPush(byte[] key, byte[]... value) {
|
||||
return delegate.lPush(key, value);
|
||||
}
|
||||
|
||||
public Long del(byte[]... keys) {
|
||||
return delegate.del(keys);
|
||||
}
|
||||
|
||||
public void close() throws DataAccessException {
|
||||
super.close();
|
||||
}
|
||||
|
||||
public String ping() {
|
||||
return delegate.ping();
|
||||
}
|
||||
|
||||
public Long sRem(byte[] key, byte[]... values) {
|
||||
return delegate.sRem(key, values);
|
||||
}
|
||||
|
||||
public Boolean zAdd(byte[] key, double score, byte[] value) {
|
||||
return delegate.zAdd(key, score, value);
|
||||
}
|
||||
|
||||
public Long publish(byte[] channel, byte[] message) {
|
||||
return delegate.publish(channel, message);
|
||||
}
|
||||
|
||||
public Boolean hSetNX(byte[] key, byte[] field, byte[] value) {
|
||||
return delegate.hSetNX(key, field, value);
|
||||
}
|
||||
|
||||
public void bgReWriteAof() {
|
||||
delegate.bgReWriteAof();
|
||||
}
|
||||
|
||||
public List<byte[]> mGet(byte[]... keys) {
|
||||
return delegate.mGet(keys);
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return delegate.isClosed();
|
||||
}
|
||||
|
||||
public Long rPushX(byte[] key, byte[] value) {
|
||||
return delegate.rPushX(key, value);
|
||||
}
|
||||
|
||||
public DataType type(byte[] key) {
|
||||
return delegate.type(key);
|
||||
}
|
||||
|
||||
public List<Boolean> scriptExists(String... scriptShas) {
|
||||
return delegate.scriptExists(scriptShas);
|
||||
}
|
||||
|
||||
public byte[] sPop(byte[] key) {
|
||||
return delegate.sPop(key);
|
||||
}
|
||||
|
||||
public void bgSave() {
|
||||
delegate.bgSave();
|
||||
}
|
||||
|
||||
public void set(byte[] key, byte[] value) {
|
||||
delegate.set(key, value);
|
||||
}
|
||||
|
||||
public void discard() {
|
||||
delegate.discard();
|
||||
}
|
||||
|
||||
public Object getNativeConnection() {
|
||||
return delegate.getNativeConnection();
|
||||
}
|
||||
|
||||
public Long zAdd(byte[] key, Set<Tuple> tuples) {
|
||||
return delegate.zAdd(key, tuples);
|
||||
}
|
||||
|
||||
public void subscribe(MessageListener listener, byte[]... channels) {
|
||||
delegate.subscribe(listener, channels);
|
||||
}
|
||||
|
||||
public Set<byte[]> keys(byte[] pattern) {
|
||||
return delegate.keys(pattern);
|
||||
}
|
||||
|
||||
public byte[] hGet(byte[] key, byte[] field) {
|
||||
return delegate.hGet(key, field);
|
||||
}
|
||||
|
||||
public Long lPushX(byte[] key, byte[] value) {
|
||||
return delegate.lPushX(key, value);
|
||||
}
|
||||
|
||||
public Long lastSave() {
|
||||
return delegate.lastSave();
|
||||
}
|
||||
|
||||
public void watch(byte[]... keys) {
|
||||
delegate.watch(keys);
|
||||
}
|
||||
|
||||
public Boolean sMove(byte[] srcKey, byte[] destKey, byte[] value) {
|
||||
return delegate.sMove(srcKey, destKey, value);
|
||||
}
|
||||
|
||||
public Boolean setNX(byte[] key, byte[] value) {
|
||||
return delegate.setNX(key, value);
|
||||
}
|
||||
|
||||
public <T> T eval(byte[] script, ReturnType returnType, int numKeys, byte[]... keysAndArgs) {
|
||||
return delegate.eval(script, returnType, numKeys, keysAndArgs);
|
||||
}
|
||||
|
||||
public boolean isQueueing() {
|
||||
return delegate.isQueueing();
|
||||
}
|
||||
|
||||
public Cursor<byte[]> scan(ScanOptions options) {
|
||||
return delegate.scan(options);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
delegate.save();
|
||||
}
|
||||
|
||||
public List<byte[]> hMGet(byte[] key, byte[]... fields) {
|
||||
return delegate.hMGet(key, fields);
|
||||
}
|
||||
|
||||
public Long zRem(byte[] key, byte[]... values) {
|
||||
return delegate.zRem(key, values);
|
||||
}
|
||||
|
||||
public Long lLen(byte[] key) {
|
||||
return delegate.lLen(key);
|
||||
}
|
||||
|
||||
public void unwatch() {
|
||||
delegate.unwatch();
|
||||
}
|
||||
|
||||
public Long dbSize() {
|
||||
return delegate.dbSize();
|
||||
}
|
||||
|
||||
public void setEx(byte[] key, long seconds, byte[] value) {
|
||||
delegate.setEx(key, seconds, value);
|
||||
}
|
||||
|
||||
public Long sCard(byte[] key) {
|
||||
return delegate.sCard(key);
|
||||
}
|
||||
|
||||
public byte[] randomKey() {
|
||||
return delegate.randomKey();
|
||||
}
|
||||
|
||||
public <T> T evalSha(String scriptSha, ReturnType returnType, int numKeys, byte[]... keysAndArgs) {
|
||||
return delegate.evalSha(scriptSha, returnType, numKeys, keysAndArgs);
|
||||
}
|
||||
|
||||
public List<byte[]> lRange(byte[] key, long begin, long end) {
|
||||
return delegate.lRange(key, begin, end);
|
||||
}
|
||||
|
||||
public void hMSet(byte[] key, Map<byte[], byte[]> hashes) {
|
||||
delegate.hMSet(key, hashes);
|
||||
}
|
||||
|
||||
public Double zIncrBy(byte[] key, double increment, byte[] value) {
|
||||
return delegate.zIncrBy(key, increment, value);
|
||||
}
|
||||
|
||||
public void flushDb() {
|
||||
delegate.flushDb();
|
||||
}
|
||||
|
||||
public Boolean sIsMember(byte[] key, byte[] value) {
|
||||
return delegate.sIsMember(key, value);
|
||||
}
|
||||
|
||||
public void pSubscribe(MessageListener listener, byte[]... patterns) {
|
||||
delegate.pSubscribe(listener, patterns);
|
||||
}
|
||||
|
||||
public void rename(byte[] oldName, byte[] newName) {
|
||||
delegate.rename(oldName, newName);
|
||||
}
|
||||
|
||||
public boolean isPipelined() {
|
||||
return delegate.isPipelined();
|
||||
}
|
||||
|
||||
public void pSetEx(byte[] key, long milliseconds, byte[] value) {
|
||||
delegate.pSetEx(key, milliseconds, value);
|
||||
}
|
||||
|
||||
public void flushAll() {
|
||||
delegate.flushAll();
|
||||
}
|
||||
|
||||
public void lTrim(byte[] key, long begin, long end) {
|
||||
delegate.lTrim(key, begin, end);
|
||||
}
|
||||
|
||||
public Long hIncrBy(byte[] key, byte[] field, long delta) {
|
||||
return delegate.hIncrBy(key, field, delta);
|
||||
}
|
||||
|
||||
public Set<byte[]> sInter(byte[]... keys) {
|
||||
return delegate.sInter(keys);
|
||||
}
|
||||
|
||||
public Boolean renameNX(byte[] oldName, byte[] newName) {
|
||||
return delegate.renameNX(oldName, newName);
|
||||
}
|
||||
|
||||
public Long zRank(byte[] key, byte[] value) {
|
||||
return delegate.zRank(key, value);
|
||||
}
|
||||
|
||||
public Properties info() {
|
||||
return delegate.info();
|
||||
}
|
||||
|
||||
public void openPipeline() {
|
||||
delegate.openPipeline();
|
||||
}
|
||||
|
||||
public void mSet(Map<byte[], byte[]> tuple) {
|
||||
delegate.mSet(tuple);
|
||||
}
|
||||
|
||||
public byte[] lIndex(byte[] key, long index) {
|
||||
return delegate.lIndex(key, index);
|
||||
}
|
||||
|
||||
public Long sInterStore(byte[] destKey, byte[]... keys) {
|
||||
return delegate.sInterStore(destKey, keys);
|
||||
}
|
||||
|
||||
public Double hIncrBy(byte[] key, byte[] field, double delta) {
|
||||
return delegate.hIncrBy(key, field, delta);
|
||||
}
|
||||
|
||||
public Long zRevRank(byte[] key, byte[] value) {
|
||||
return delegate.zRevRank(key, value);
|
||||
}
|
||||
|
||||
public Boolean expire(byte[] key, long seconds) {
|
||||
return delegate.expire(key, seconds);
|
||||
}
|
||||
|
||||
public Properties info(String section) {
|
||||
return delegate.info(section);
|
||||
}
|
||||
|
||||
public Boolean mSetNX(Map<byte[], byte[]> tuple) {
|
||||
return delegate.mSetNX(tuple);
|
||||
}
|
||||
|
||||
public Long lInsert(byte[] key, Position where, byte[] pivot, byte[] value) {
|
||||
return delegate.lInsert(key, where, pivot, value);
|
||||
}
|
||||
|
||||
public Set<byte[]> sUnion(byte[]... keys) {
|
||||
return delegate.sUnion(keys);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
delegate.shutdown();
|
||||
}
|
||||
|
||||
public Boolean pExpire(byte[] key, long millis) {
|
||||
return delegate.pExpire(key, millis);
|
||||
}
|
||||
|
||||
public Boolean hExists(byte[] key, byte[] field) {
|
||||
return delegate.hExists(key, field);
|
||||
}
|
||||
|
||||
public Set<byte[]> zRange(byte[] key, long begin, long end) {
|
||||
return delegate.zRange(key, begin, end);
|
||||
}
|
||||
|
||||
public void shutdown(ShutdownOption option) {
|
||||
delegate.shutdown(option);
|
||||
}
|
||||
|
||||
public Long sUnionStore(byte[] destKey, byte[]... keys) {
|
||||
return delegate.sUnionStore(destKey, keys);
|
||||
}
|
||||
|
||||
public Long incr(byte[] key) {
|
||||
return delegate.incr(key);
|
||||
}
|
||||
|
||||
public Long hDel(byte[] key, byte[]... fields) {
|
||||
return delegate.hDel(key, fields);
|
||||
}
|
||||
|
||||
public Boolean expireAt(byte[] key, long unixTime) {
|
||||
return delegate.expireAt(key, unixTime);
|
||||
}
|
||||
|
||||
public List<String> getConfig(String pattern) {
|
||||
return delegate.getConfig(pattern);
|
||||
}
|
||||
|
||||
public void lSet(byte[] key, long index, byte[] value) {
|
||||
delegate.lSet(key, index, value);
|
||||
}
|
||||
|
||||
public Set<Tuple> zRangeWithScores(byte[] key, long begin, long end) {
|
||||
return delegate.zRangeWithScores(key, begin, end);
|
||||
}
|
||||
|
||||
public Long incrBy(byte[] key, long value) {
|
||||
return delegate.incrBy(key, value);
|
||||
}
|
||||
|
||||
public Set<byte[]> sDiff(byte[]... keys) {
|
||||
return delegate.sDiff(keys);
|
||||
}
|
||||
|
||||
public Long hLen(byte[] key) {
|
||||
return delegate.hLen(key);
|
||||
}
|
||||
|
||||
public List<Object> closePipeline() throws RedisPipelineException {
|
||||
return delegate.closePipeline();
|
||||
}
|
||||
|
||||
public void setConfig(String param, String value) {
|
||||
delegate.setConfig(param, value);
|
||||
}
|
||||
|
||||
public Boolean pExpireAt(byte[] key, long unixTimeInMillis) {
|
||||
return delegate.pExpireAt(key, unixTimeInMillis);
|
||||
}
|
||||
|
||||
public Long lRem(byte[] key, long count, byte[] value) {
|
||||
return delegate.lRem(key, count, value);
|
||||
}
|
||||
|
||||
public Double incrBy(byte[] key, double value) {
|
||||
return delegate.incrBy(key, value);
|
||||
}
|
||||
|
||||
public Long sDiffStore(byte[] destKey, byte[]... keys) {
|
||||
return delegate.sDiffStore(destKey, keys);
|
||||
}
|
||||
|
||||
public Set<byte[]> zRangeByScore(byte[] key, double min, double max) {
|
||||
return delegate.zRangeByScore(key, min, max);
|
||||
}
|
||||
|
||||
public Set<byte[]> hKeys(byte[] key) {
|
||||
return delegate.hKeys(key);
|
||||
}
|
||||
|
||||
public void resetConfigStats() {
|
||||
delegate.resetConfigStats();
|
||||
}
|
||||
|
||||
public Long decr(byte[] key) {
|
||||
return delegate.decr(key);
|
||||
}
|
||||
|
||||
public List<byte[]> hVals(byte[] key) {
|
||||
return delegate.hVals(key);
|
||||
}
|
||||
|
||||
public Boolean persist(byte[] key) {
|
||||
return delegate.persist(key);
|
||||
}
|
||||
|
||||
public byte[] lPop(byte[] key) {
|
||||
return delegate.lPop(key);
|
||||
}
|
||||
|
||||
public Set<byte[]> sMembers(byte[] key) {
|
||||
return delegate.sMembers(key);
|
||||
}
|
||||
|
||||
public Long decrBy(byte[] key, long value) {
|
||||
return delegate.decrBy(key, value);
|
||||
}
|
||||
|
||||
public Set<Tuple> zRangeByScoreWithScores(byte[] key, double min, double max) {
|
||||
return delegate.zRangeByScoreWithScores(key, min, max);
|
||||
}
|
||||
|
||||
public Long time() {
|
||||
return delegate.time();
|
||||
}
|
||||
|
||||
public Map<byte[], byte[]> hGetAll(byte[] key) {
|
||||
return delegate.hGetAll(key);
|
||||
}
|
||||
|
||||
public Boolean move(byte[] key, int dbIndex) {
|
||||
return delegate.move(key, dbIndex);
|
||||
}
|
||||
|
||||
public byte[] sRandMember(byte[] key) {
|
||||
return delegate.sRandMember(key);
|
||||
}
|
||||
|
||||
public byte[] rPop(byte[] key) {
|
||||
return delegate.rPop(key);
|
||||
}
|
||||
|
||||
public void killClient(String host, int port) {
|
||||
delegate.killClient(host, port);
|
||||
}
|
||||
|
||||
public Long append(byte[] key, byte[] value) {
|
||||
return delegate.append(key, value);
|
||||
}
|
||||
|
||||
public Cursor<Entry<byte[], byte[]>> hScan(byte[] key, ScanOptions options) {
|
||||
return delegate.hScan(key, options);
|
||||
}
|
||||
|
||||
public List<byte[]> sRandMember(byte[] key, long count) {
|
||||
return delegate.sRandMember(key, count);
|
||||
}
|
||||
|
||||
public Long ttl(byte[] key) {
|
||||
return delegate.ttl(key);
|
||||
}
|
||||
|
||||
public List<byte[]> bLPop(int timeout, byte[]... keys) {
|
||||
return delegate.bLPop(timeout, keys);
|
||||
}
|
||||
|
||||
public Set<byte[]> zRangeByScore(byte[] key, double min, double max, long offset, long count) {
|
||||
return delegate.zRangeByScore(key, min, max, offset, count);
|
||||
}
|
||||
|
||||
public byte[] getRange(byte[] key, long begin, long end) {
|
||||
return delegate.getRange(key, begin, end);
|
||||
}
|
||||
|
||||
public void setClientName(byte[] name) {
|
||||
delegate.setClientName(name);
|
||||
}
|
||||
|
||||
public Long pTtl(byte[] key) {
|
||||
return delegate.pTtl(key);
|
||||
}
|
||||
|
||||
public Cursor<byte[]> sScan(byte[] key, ScanOptions options) {
|
||||
return delegate.sScan(key, options);
|
||||
}
|
||||
|
||||
public String getClientName() {
|
||||
return delegate.getClientName();
|
||||
}
|
||||
|
||||
public List<byte[]> sort(byte[] key, SortParameters params) {
|
||||
return delegate.sort(key, params);
|
||||
}
|
||||
|
||||
public List<byte[]> bRPop(int timeout, byte[]... keys) {
|
||||
return delegate.bRPop(timeout, keys);
|
||||
}
|
||||
|
||||
public void setRange(byte[] key, byte[] value, long offset) {
|
||||
delegate.setRange(key, value, offset);
|
||||
}
|
||||
|
||||
public Set<Tuple> zRangeByScoreWithScores(byte[] key, double min, double max, long offset, long count) {
|
||||
return delegate.zRangeByScoreWithScores(key, min, max, offset, count);
|
||||
}
|
||||
|
||||
public List<RedisClientInfo> getClientList() {
|
||||
return delegate.getClientList();
|
||||
}
|
||||
|
||||
public Long sort(byte[] key, SortParameters params, byte[] storeKey) {
|
||||
return delegate.sort(key, params, storeKey);
|
||||
}
|
||||
|
||||
public Boolean getBit(byte[] key, long offset) {
|
||||
return delegate.getBit(key, offset);
|
||||
}
|
||||
|
||||
public void slaveOf(String host, int port) {
|
||||
delegate.slaveOf(host, port);
|
||||
}
|
||||
|
||||
public byte[] rPopLPush(byte[] srcKey, byte[] dstKey) {
|
||||
return delegate.rPopLPush(srcKey, dstKey);
|
||||
}
|
||||
|
||||
public Set<byte[]> zRevRange(byte[] key, long begin, long end) {
|
||||
return delegate.zRevRange(key, begin, end);
|
||||
}
|
||||
|
||||
public byte[] dump(byte[] key) {
|
||||
return delegate.dump(key);
|
||||
}
|
||||
|
||||
public Boolean setBit(byte[] key, long offset, boolean value) {
|
||||
return delegate.setBit(key, offset, value);
|
||||
}
|
||||
|
||||
public void slaveOfNoOne() {
|
||||
delegate.slaveOfNoOne();
|
||||
}
|
||||
|
||||
public void restore(byte[] key, long ttlInMillis, byte[] serializedValue) {
|
||||
delegate.restore(key, ttlInMillis, serializedValue);
|
||||
}
|
||||
|
||||
public byte[] bRPopLPush(int timeout, byte[] srcKey, byte[] dstKey) {
|
||||
return delegate.bRPopLPush(timeout, srcKey, dstKey);
|
||||
}
|
||||
|
||||
public Set<Tuple> zRevRangeWithScores(byte[] key, long begin, long end) {
|
||||
return delegate.zRevRangeWithScores(key, begin, end);
|
||||
}
|
||||
|
||||
public Long bitCount(byte[] key) {
|
||||
return delegate.bitCount(key);
|
||||
}
|
||||
|
||||
public Set<byte[]> zRevRangeByScore(byte[] key, double min, double max) {
|
||||
return delegate.zRevRangeByScore(key, min, max);
|
||||
}
|
||||
|
||||
public Long bitCount(byte[] key, long begin, long end) {
|
||||
return delegate.bitCount(key, begin, end);
|
||||
}
|
||||
|
||||
public Set<Tuple> zRevRangeByScoreWithScores(byte[] key, double min, double max) {
|
||||
return delegate.zRevRangeByScoreWithScores(key, min, max);
|
||||
}
|
||||
|
||||
public Long bitOp(BitOperation op, byte[] destination, byte[]... keys) {
|
||||
return delegate.bitOp(op, destination, keys);
|
||||
}
|
||||
|
||||
public Long strLen(byte[] key) {
|
||||
return delegate.strLen(key);
|
||||
}
|
||||
|
||||
public Set<byte[]> zRevRangeByScore(byte[] key, double min, double max, long offset, long count) {
|
||||
return delegate.zRevRangeByScore(key, min, max, offset, count);
|
||||
}
|
||||
|
||||
public Set<Tuple> zRevRangeByScoreWithScores(byte[] key, double min, double max, long offset, long count) {
|
||||
return delegate.zRevRangeByScoreWithScores(key, min, max, offset, count);
|
||||
}
|
||||
|
||||
public Long zCount(byte[] key, double min, double max) {
|
||||
return delegate.zCount(key, min, max);
|
||||
}
|
||||
|
||||
public Long zCard(byte[] key) {
|
||||
return delegate.zCard(key);
|
||||
}
|
||||
|
||||
public Double zScore(byte[] key, byte[] value) {
|
||||
return delegate.zScore(key, value);
|
||||
}
|
||||
|
||||
public Long zRemRange(byte[] key, long begin, long end) {
|
||||
return delegate.zRemRange(key, begin, end);
|
||||
}
|
||||
|
||||
public Long zRemRangeByScore(byte[] key, double min, double max) {
|
||||
return delegate.zRemRangeByScore(key, min, max);
|
||||
}
|
||||
|
||||
public Long zUnionStore(byte[] destKey, byte[]... sets) {
|
||||
return delegate.zUnionStore(destKey, sets);
|
||||
}
|
||||
|
||||
public Long zUnionStore(byte[] destKey, Aggregate aggregate, int[] weights, byte[]... sets) {
|
||||
return delegate.zUnionStore(destKey, aggregate, weights, sets);
|
||||
}
|
||||
|
||||
public Long zInterStore(byte[] destKey, byte[]... sets) {
|
||||
return delegate.zInterStore(destKey, sets);
|
||||
}
|
||||
|
||||
public Long zInterStore(byte[] destKey, Aggregate aggregate, int[] weights, byte[]... sets) {
|
||||
return delegate.zInterStore(destKey, aggregate, weights, sets);
|
||||
}
|
||||
|
||||
public Cursor<Tuple> zScan(byte[] key, ScanOptions options) {
|
||||
return delegate.zScan(key, options);
|
||||
}
|
||||
|
||||
public RedisConnection getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RedisSentinelConnection getSentinelConnection(RedisNode sentinel) {
|
||||
if (ObjectUtils.nullSafeEquals(this.activeNode, sentinel)) {
|
||||
return this.sentinelConnection;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import org.hamcrest.core.AllOf;
|
||||
import org.hamcrest.core.IsCollectionContaining;
|
||||
import org.hamcrest.core.IsInstanceOf;
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
@@ -40,9 +41,13 @@ import org.springframework.data.redis.connection.DefaultStringTuple;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.ReturnType;
|
||||
import org.springframework.data.redis.connection.StringRedisConnection.StringTuple;
|
||||
import org.springframework.data.redis.test.util.RedisSentinelRule;
|
||||
import org.springframework.data.redis.test.util.RedisSentinelRule.SentinelsAvailable;
|
||||
import org.springframework.data.redis.test.util.RelaxedJUnit4ClassRunner;
|
||||
import org.springframework.data.redis.test.util.RequiresRedisSentinel;
|
||||
import org.springframework.test.annotation.IfProfileValue;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
@@ -60,6 +65,8 @@ import redis.clients.jedis.JedisPoolConfig;
|
||||
@ContextConfiguration
|
||||
public class JedisConnectionIntegrationTests extends AbstractConnectionIntegrationTests {
|
||||
|
||||
public @Rule RedisSentinelRule sentinelRule = RedisSentinelRule.withDefaultConfig().dynamicModeSelection();
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
try {
|
||||
@@ -366,4 +373,16 @@ public class JedisConnectionIntegrationTests extends AbstractConnectionIntegrati
|
||||
assertTrue(String.format("difference between millis=%s and ttl=%s should not be greater than 20ms but is %s",
|
||||
millis, ttl, millis - ttl), millis - ttl < 20L);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
@RequiresRedisSentinel(SentinelsAvailable.ONE_ACTIVE)
|
||||
public void shouldReturnSentinelCommandsWhenWhenActiveSentinelFound() {
|
||||
|
||||
((JedisConnection) byteConnection).setSentinelConfiguration(new RedisSentinelConfiguration().master("mymaster")
|
||||
.sentinel("127.0.0.1", 26379).sentinel("127.0.0.1", 26380));
|
||||
assertThat(connection.getSentinelConnection(), notNullValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.redis.connection.AbstractConnectionUnitTestBase;
|
||||
import org.springframework.data.redis.connection.RedisServerCommands.ShutdownOption;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionUnitTestSuite.JedisConnectionPipelineUnitTests;
|
||||
@@ -115,8 +116,6 @@ public class JedisConnectionUnitTestSuite {
|
||||
}
|
||||
|
||||
/**
|
||||
* <<<<<<< HEAD
|
||||
*
|
||||
* @see DATAREDIS-267
|
||||
*/
|
||||
@Test
|
||||
@@ -163,6 +162,15 @@ public class JedisConnectionUnitTestSuite {
|
||||
connection.slaveOfNoOne();
|
||||
verifyNativeConnectionInvocation().slaveofNoOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test(expected = InvalidDataAccessResourceUsageException.class)
|
||||
public void shouldThrowExceptionWhenAccessingRedisSentinelsCommandsWhenNoSentinelsConfigured() {
|
||||
connection.getSentinelConnection();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class JedisConnectionPipelineUnitTests extends JedisConnectionUnitTests {
|
||||
|
||||
@@ -15,12 +15,19 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.IsEqual.*;
|
||||
import static org.hamcrest.core.IsNull.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.redis.connection.RedisServer;
|
||||
import org.springframework.data.redis.core.types.RedisClientInfo;
|
||||
|
||||
/**
|
||||
@@ -59,4 +66,80 @@ public class JedisConvertersUnitTests {
|
||||
|
||||
assertThat(JedisConverters.toListOfRedisClientInformation(sb.toString()).size(), equalTo(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void convertsSingleMapToRedisServerReturnsCollectionCorrectly() {
|
||||
|
||||
Map<String, String> values = getRedisServerInfoMap("mymaster", 23697);
|
||||
List<RedisServer> servers = JedisConverters.toListOfRedisServer(Collections.singletonList(values));
|
||||
|
||||
assertThat(servers.size(), is(1));
|
||||
verifyRedisServerInfo(servers.get(0), values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void convertsMultipleMapsToRedisServerReturnsCollectionCorrectly() {
|
||||
|
||||
List<Map<String, String>> vals = Arrays.asList(getRedisServerInfoMap("mymaster", 23697),
|
||||
getRedisServerInfoMap("yourmaster", 23680));
|
||||
List<RedisServer> servers = JedisConverters.toListOfRedisServer(vals);
|
||||
|
||||
assertThat(servers.size(), is(vals.size()));
|
||||
for (int i = 0; i < vals.size(); i++) {
|
||||
verifyRedisServerInfo(servers.get(i), vals.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void convertsRedisServersCorrectlyWhenGivenAnEmptyList() {
|
||||
assertThat(JedisConverters.toListOfRedisServer(Collections.<Map<String, String>> emptyList()), notNullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void convertsRedisServersCorrectlyWhenGivenNull() {
|
||||
assertThat(JedisConverters.toListOfRedisServer(null), notNullValue());
|
||||
}
|
||||
|
||||
private void verifyRedisServerInfo(RedisServer server, Map<String, String> values) {
|
||||
|
||||
for (Map.Entry<String, String> entry : values.entrySet()) {
|
||||
assertThat(server.get(entry.getKey()), equalTo(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, String> getRedisServerInfoMap(String name, int port) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("name", name);
|
||||
map.put("ip", "127.0.0.1");
|
||||
map.put("port", Integer.toString(port));
|
||||
map.put("runid", "768c2926e5148d208713bf17cd5821e10f5388e2");
|
||||
map.put("flags", "master");
|
||||
map.put("pending-commands", "0");
|
||||
map.put("last-ping-sent", "0");
|
||||
map.put("last-ok-ping-reply", "534");
|
||||
map.put("last-ping-reply", "534");
|
||||
map.put("down-after-milliseconds", "30000");
|
||||
map.put("info-refresh", "147");
|
||||
map.put("role-reported", "master");
|
||||
map.put("role-reported-time", "41248339");
|
||||
map.put("config-epoch", "7");
|
||||
map.put("num-slaves", "2");
|
||||
map.put("num-other-sentinels", "2");
|
||||
map.put("quorum", "2");
|
||||
map.put("failover-timeout", "180000");
|
||||
map.put("parallel-syncs", "1");
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2014 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.connection.jedis;
|
||||
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
import org.springframework.data.redis.connection.RedisNode.RedisNodeBuilder;
|
||||
import org.springframework.data.redis.connection.RedisServer;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class JedisSentinelConnectionUnitTests {
|
||||
|
||||
private @Mock Jedis jedisMock;
|
||||
|
||||
private JedisSentinelConnection connection;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.connection = new JedisSentinelConnection(jedisMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void shouldConnectAfterCreation() {
|
||||
verify(jedisMock, times(1)).connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
@Test
|
||||
public void shouldNotConnectIfAlreadyConnected() {
|
||||
|
||||
Jedis yetAnotherJedisMock = mock(Jedis.class);
|
||||
when(yetAnotherJedisMock.isConnected()).thenReturn(true);
|
||||
|
||||
new JedisSentinelConnection(yetAnotherJedisMock);
|
||||
|
||||
verify(yetAnotherJedisMock, never()).connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void failoverShouldBeSentCorrectly() {
|
||||
|
||||
connection.failover(new RedisNodeBuilder().withName("mymaster").build());
|
||||
verify(jedisMock, times(1)).sentinelFailover(eq("mymaster"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void failoverShouldThrowExceptionIfMasterNodeIsNull() {
|
||||
connection.failover(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void failoverShouldThrowExceptionIfMasterNodeNameIsEmpty() {
|
||||
connection.failover(new RedisNodeBuilder().build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void mastersShouldReadMastersCorrectly() {
|
||||
|
||||
connection.masters();
|
||||
verify(jedisMock, times(1)).sentinelMasters();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void shouldReadSlavesCorrectly() {
|
||||
|
||||
connection.slaves("mymaster");
|
||||
verify(jedisMock, times(1)).sentinelSlaves(eq("mymaster"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void shouldReadSlavesCorrectlyWhenGivenNamedNode() {
|
||||
|
||||
connection.slaves(new RedisNodeBuilder().withName("mymaster").build());
|
||||
verify(jedisMock, times(1)).sentinelSlaves(eq("mymaster"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void readSlavesShouldThrowExceptionWhenGivenEmptyMasterName() {
|
||||
connection.slaves("");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void readSlavesShouldThrowExceptionWhenGivenNull() {
|
||||
connection.slaves((RedisNode) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void readSlavesShouldThrowExceptionWhenNodeWithoutName() {
|
||||
connection.slaves(new RedisNodeBuilder().build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void shouldRemoveMasterCorrectlyWhenGivenNamedNode() {
|
||||
|
||||
connection.remove(new RedisNodeBuilder().withName("mymaster").build());
|
||||
verify(jedisMock, times(1)).sentinelRemove(eq("mymaster"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void removeShouldThrowExceptionWhenGivenEmptyMasterName() {
|
||||
connection.remove("");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void removeShouldThrowExceptionWhenGivenNull() {
|
||||
connection.remove((RedisNode) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void removeShouldThrowExceptionWhenNodeWithoutName() {
|
||||
connection.remove(new RedisNodeBuilder().build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void monitorShouldBeSentCorrectly() {
|
||||
|
||||
RedisServer server = new RedisServer("127.0.0.1", 6382);
|
||||
server.setName("anothermaster");
|
||||
server.setQuorum(3L);
|
||||
|
||||
connection.monitor(server);
|
||||
verify(jedisMock, times(1)).sentinelMonitor(eq("anothermaster"), eq("127.0.0.1"), eq(6382), eq(3));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
@@ -23,17 +29,29 @@ import org.junit.Test;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.connection.AbstractConnectionIntegrationTests;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConnection;
|
||||
import org.springframework.data.redis.connection.RedisServer;
|
||||
import org.springframework.data.redis.connection.ReturnType;
|
||||
import org.springframework.data.redis.test.util.RedisSentinelRule;
|
||||
import org.springframework.test.annotation.IfProfileValue;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class JedisSentinelIntegrationTests extends AbstractConnectionIntegrationTests {
|
||||
|
||||
private static final RedisSentinelConfiguration SENTINEL_CONFIG = new RedisSentinelConfiguration().master("mymaster")
|
||||
.sentinel("127.0.0.1", 26379).sentinel("127.0.0.1", 26380);
|
||||
|
||||
private static final String MASTER_NAME = "mymaster";
|
||||
private static final RedisServer SENTINEL_0 = new RedisServer("127.0.0.1", 26379);
|
||||
private static final RedisServer SENTINEL_1 = new RedisServer("127.0.0.1", 26380);
|
||||
|
||||
private static final RedisServer SLAVE_0 = new RedisServer("127.0.0.1", 6380);
|
||||
private static final RedisServer SLAVE_1 = new RedisServer("127.0.0.1", 6381);
|
||||
|
||||
private static final RedisSentinelConfiguration SENTINEL_CONFIG = new RedisSentinelConfiguration() //
|
||||
.master(MASTER_NAME)
|
||||
.sentinel(SENTINEL_0)
|
||||
.sentinel(SENTINEL_1);
|
||||
|
||||
public @Rule RedisSentinelRule sentinelRule = RedisSentinelRule.forConfig(SENTINEL_CONFIG).oneActive();
|
||||
|
||||
@@ -102,4 +120,32 @@ public class JedisSentinelIntegrationTests extends AbstractConnectionIntegration
|
||||
public void testErrorInTx() {
|
||||
super.testErrorInTx();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void shouldReadMastersCorrectly() {
|
||||
|
||||
List<RedisServer> servers = (List<RedisServer>) connectionFactory.getSentinelConnection().masters();
|
||||
assertThat(servers.size(), is(1));
|
||||
assertThat(servers.get(0).getName(),is(MASTER_NAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-330
|
||||
*/
|
||||
@Test
|
||||
public void shouldReadSlavesOfMastersCorrectly() {
|
||||
|
||||
RedisSentinelConnection sentinelConnection = connectionFactory.getSentinelConnection();
|
||||
|
||||
List<RedisServer> servers = (List<RedisServer>) sentinelConnection.masters();
|
||||
assertThat(servers.size(), is(1));
|
||||
|
||||
Collection<RedisServer> slaves = sentinelConnection.slaves(servers.get(0));
|
||||
assertThat(slaves.size(), is(2));
|
||||
assertThat(slaves, hasItems(SLAVE_0, SLAVE_1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,24 +29,18 @@ import redis.clients.jedis.Jedis;
|
||||
*/
|
||||
public class RedisSentinelRule implements TestRule {
|
||||
|
||||
enum VerificationMode {
|
||||
ALL_ACTIVE, ONE_ACTIVE, NO_SENTINEL
|
||||
public enum SentinelsAvailable {
|
||||
ALL_ACTIVE, ONE_ACTIVE, NONE_ACTIVE
|
||||
}
|
||||
|
||||
private static final RedisSentinelConfiguration DEFAULT_SENTINEL_CONFIG = new RedisSentinelConfiguration()
|
||||
.master("mymaster").sentinel("127.0.0.1", 26379).sentinel("127.0.0.1", 26380).sentinel("127.0.0.1", 26381);
|
||||
|
||||
private RedisSentinelConfiguration sentinelConfig;
|
||||
private VerificationMode mode;
|
||||
private SentinelsAvailable requiredSentinels;
|
||||
|
||||
protected RedisSentinelRule(RedisSentinelConfiguration config) {
|
||||
this(config, VerificationMode.ALL_ACTIVE);
|
||||
}
|
||||
|
||||
protected RedisSentinelRule(RedisSentinelConfiguration config, VerificationMode mode) {
|
||||
|
||||
this.sentinelConfig = config;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,8 +63,8 @@ public class RedisSentinelRule implements TestRule {
|
||||
}
|
||||
|
||||
public RedisSentinelRule sentinelsDisabled() {
|
||||
|
||||
this.mode = VerificationMode.NO_SENTINEL;
|
||||
|
||||
this.requiredSentinels = SentinelsAvailable.NONE_ACTIVE;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -80,8 +74,8 @@ public class RedisSentinelRule implements TestRule {
|
||||
* @return
|
||||
*/
|
||||
public RedisSentinelRule allActive() {
|
||||
|
||||
this.mode = VerificationMode.ALL_ACTIVE;
|
||||
|
||||
this.requiredSentinels = SentinelsAvailable.ALL_ACTIVE;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -91,25 +85,46 @@ public class RedisSentinelRule implements TestRule {
|
||||
* @return
|
||||
*/
|
||||
public RedisSentinelRule oneActive() {
|
||||
|
||||
this.mode = VerificationMode.ONE_ACTIVE;
|
||||
|
||||
this.requiredSentinels = SentinelsAvailable.ONE_ACTIVE;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will only check {@link RedisSentinelConfiguration} configuration in case {@link RequiresRedisSentinel} is detected
|
||||
* on test method.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public RedisSentinelRule dynamicModeSelection() {
|
||||
this.requiredSentinels = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(final Statement base, Description description) {
|
||||
public Statement apply(final Statement base, final Description description) {
|
||||
|
||||
return new Statement() {
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
verify();
|
||||
|
||||
if (description.isTest()) {
|
||||
RequiresRedisSentinel sentinels = description.getAnnotation(RequiresRedisSentinel.class);
|
||||
if (RedisSentinelRule.this.requiredSentinels != null || sentinels != null) {
|
||||
verify(sentinels != null ? sentinels.value() : RedisSentinelRule.this.requiredSentinels);
|
||||
}
|
||||
|
||||
} else {
|
||||
verify(RedisSentinelRule.this.requiredSentinels);
|
||||
}
|
||||
|
||||
base.evaluate();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void verify() {
|
||||
private void verify(SentinelsAvailable verificationMode) {
|
||||
|
||||
int failed = 0;
|
||||
for (RedisNode node : sentinelConfig.getSentinels()) {
|
||||
@@ -119,19 +134,19 @@ public class RedisSentinelRule implements TestRule {
|
||||
}
|
||||
|
||||
if (failed > 0) {
|
||||
if (VerificationMode.ALL_ACTIVE.equals(mode)) {
|
||||
if (SentinelsAvailable.ALL_ACTIVE.equals(verificationMode)) {
|
||||
throw new AssumptionViolatedException(String.format(
|
||||
"Expected all Redis Sentinels to respone but %s of %s did not responde", failed, sentinelConfig
|
||||
.getSentinels().size()));
|
||||
}
|
||||
|
||||
if (VerificationMode.ONE_ACTIVE.equals(mode) && sentinelConfig.getSentinels().size() - 1 < failed) {
|
||||
if (SentinelsAvailable.ONE_ACTIVE.equals(verificationMode) && sentinelConfig.getSentinels().size() - 1 < failed) {
|
||||
throw new AssumptionViolatedException(
|
||||
"Expected at least one sentinel to respond but it seems all are offline - Game Over!");
|
||||
}
|
||||
}
|
||||
|
||||
if (VerificationMode.NO_SENTINEL.equals(mode) && failed != sentinelConfig.getSentinels().size()) {
|
||||
if (SentinelsAvailable.NONE_ACTIVE.equals(verificationMode) && failed != sentinelConfig.getSentinels().size()) {
|
||||
throw new AssumptionViolatedException(String.format(
|
||||
"Expected to have no sentinels online but found that %s are still alive.", (sentinelConfig.getSentinels()
|
||||
.size() - failed)));
|
||||
@@ -139,30 +154,30 @@ public class RedisSentinelRule implements TestRule {
|
||||
}
|
||||
|
||||
private boolean isAvailable(RedisNode node) {
|
||||
|
||||
|
||||
Jedis jedis = null;
|
||||
try {
|
||||
|
||||
|
||||
jedis = new Jedis(node.getHost(), node.getPort());
|
||||
jedis.connect();
|
||||
jedis.ping();
|
||||
} catch (Exception e) {
|
||||
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
|
||||
|
||||
if (jedis != null) {
|
||||
try {
|
||||
|
||||
|
||||
jedis.disconnect();
|
||||
jedis.close();
|
||||
} catch (Exception e) {
|
||||
|
||||
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2014 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.test.util;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.redis.test.util.RedisSentinelRule.SentinelsAvailable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@Documented
|
||||
@Target({ ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequiresRedisSentinel {
|
||||
|
||||
SentinelsAvailable value() default SentinelsAvailable.ALL_ACTIVE;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user