DATAREDIS-491 - Add configuration option to tune KeyspaceEventMessageListener usage.

We now allow more fine grained setup for usage of Redis keyspace events. This can be done programmatically via the RedisKeyValueAdapter or using `@EnableRedisRepositories.enableKeyspaceEvents`.

Original pull request: #193.
This commit is contained in:
Christoph Strobl
2016-04-27 12:51:09 +02:00
committed by Mark Paluch
parent 46f07d7056
commit 62c704d58a
6 changed files with 262 additions and 38 deletions

View File

@@ -69,6 +69,7 @@ public class RedisKeyValueAdapterTests {
mappingContext.afterPropertiesSet();
adapter = new RedisKeyValueAdapter(template, mappingContext);
adapter.afterPropertiesSet();
template.execute(new RedisCallback<Void>() {

View File

@@ -16,51 +16,73 @@
package org.springframework.data.redis.core;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import static org.springframework.test.util.ReflectionTestUtils.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.After;
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.annotation.Id;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisKeyValueAdapter.EnableKeyspaceEvents;
import org.springframework.data.redis.core.convert.Bucket;
import org.springframework.data.redis.core.convert.KeyspaceConfiguration;
import org.springframework.data.redis.core.convert.MappingConfiguration;
import org.springframework.data.redis.core.convert.RedisData;
import org.springframework.data.redis.core.convert.SimpleIndexedPropertyValue;
import org.springframework.data.redis.core.index.IndexConfiguration;
import org.springframework.data.redis.core.mapping.RedisMappingContext;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
/**
* Unit tests for {@link RedisKeyValueAdapter}.
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class RedisKeyValueAdapterUnitTests {
RedisTemplate<?, ?> redisTemplate;
RedisKeyValueAdapter redisKeyValueAdapter;
RedisKeyValueAdapter adapter;
RedisTemplate<?, ?> template;
RedisMappingContext context;
@Mock JedisConnectionFactory jedisConnectionFactoryMock;
@Mock RedisConnection redisConnectionMock;
@Before
public void setUp() throws Exception {
redisTemplate = new RedisTemplate<Object, Object>();
redisTemplate.setConnectionFactory(jedisConnectionFactoryMock);
redisTemplate.afterPropertiesSet();
template = new RedisTemplate<Object, Object>();
template.setConnectionFactory(jedisConnectionFactoryMock);
template.afterPropertiesSet();
when(jedisConnectionFactoryMock.getConnection()).thenReturn(redisConnectionMock);
when(redisConnectionMock.getConfig("notify-keyspace-events"))
.thenReturn(Arrays.asList("notify-keyspace-events", "KEA"));
redisKeyValueAdapter = new RedisKeyValueAdapter(redisTemplate);
context = new RedisMappingContext(new MappingConfiguration(new IndexConfiguration(), new KeyspaceConfiguration()));
context.afterPropertiesSet();
adapter = new RedisKeyValueAdapter(template, context);
adapter.afterPropertiesSet();
}
@After
public void tearDown() throws Exception {
adapter.destroy();
}
/**
@@ -69,7 +91,7 @@ public class RedisKeyValueAdapterUnitTests {
@Test
public void destroyShouldNotDestroyConnectionFactory() throws Exception {
redisKeyValueAdapter.destroy();
adapter.destroy();
verify(jedisConnectionFactoryMock, never()).destroy();
}
@@ -87,7 +109,7 @@ public class RedisKeyValueAdapterUnitTests {
.thenReturn(new LinkedHashSet<byte[]>(Arrays.asList("persons:firstname:rand".getBytes())));
when(redisConnectionMock.del((byte[][]) anyVararg())).thenReturn(1L);
redisKeyValueAdapter.put("1", rd, "persons");
adapter.put("1", rd, "persons");
verify(redisConnectionMock, times(1)).sRem(any(byte[].class), any(byte[].class));
}
@@ -105,8 +127,88 @@ public class RedisKeyValueAdapterUnitTests {
.thenReturn(new LinkedHashSet<byte[]>(Arrays.asList("persons:firstname:rand".getBytes())));
when(redisConnectionMock.del((byte[][]) anyVararg())).thenReturn(0L);
redisKeyValueAdapter.put("1", rd, "persons");
adapter.put("1", rd, "persons");
verify(redisConnectionMock, never()).sRem(any(byte[].class), (byte[][]) anyVararg());
}
/**
* @see DATAREDIS-491
*/
@Test
public void shouldInitKeyExpirationListenerOnStartup() throws Exception{
adapter.destroy();
adapter = new RedisKeyValueAdapter(template, context);
adapter.setEnableKeyspaceEvents(EnableKeyspaceEvents.ON_STARTUP);
adapter.afterPropertiesSet();
KeyExpirationEventMessageListener listener = ((AtomicReference<KeyExpirationEventMessageListener>) getField(adapter,
"expirationListener")).get();
assertThat(listener, notNullValue());
}
/**
* @see DATAREDIS-491
*/
@Test
public void shouldInitKeyExpirationListenerOnFirstPutWithTtl() throws Exception {
adapter.destroy();
adapter = new RedisKeyValueAdapter(template, context);
adapter.setEnableKeyspaceEvents(EnableKeyspaceEvents.ON_DEMAND);
adapter.afterPropertiesSet();
KeyExpirationEventMessageListener listener = ((AtomicReference<KeyExpirationEventMessageListener>) getField(adapter,
"expirationListener")).get();
assertThat(listener, nullValue());
adapter.put("should-NOT-start-listener", new WithoutTimeToLive(), "keyspace");
listener = ((AtomicReference<KeyExpirationEventMessageListener>) getField(adapter, "expirationListener")).get();
assertThat(listener, nullValue());
adapter.put("should-start-listener", new WithTimeToLive(), "keyspace");
listener = ((AtomicReference<KeyExpirationEventMessageListener>) getField(adapter, "expirationListener")).get();
assertThat(listener, notNullValue());
}
/**
* @see DATAREDIS-491
*/
@Test
public void shouldNeverInitKeyExpirationListener() throws Exception {
adapter.destroy();
adapter = new RedisKeyValueAdapter(template, context);
adapter.setEnableKeyspaceEvents(EnableKeyspaceEvents.OFF);
adapter.afterPropertiesSet();
KeyExpirationEventMessageListener listener = ((AtomicReference<KeyExpirationEventMessageListener>) getField(adapter,
"expirationListener")).get();
assertThat(listener, nullValue());
adapter.put("should-NOT-start-listener", new WithoutTimeToLive(), "keyspace");
listener = ((AtomicReference<KeyExpirationEventMessageListener>) getField(adapter, "expirationListener")).get();
assertThat(listener, nullValue());
adapter.put("should-start-listener", new WithTimeToLive(), "keyspace");
listener = ((AtomicReference<KeyExpirationEventMessageListener>) getField(adapter, "expirationListener")).get();
assertThat(listener, nullValue());
}
static class WithoutTimeToLive {
@Id String id;
}
@RedisHash(timeToLive = 10)
static class WithTimeToLive {
@Id String id;
}
}

View File

@@ -19,8 +19,11 @@ import static org.junit.Assert.*;
import java.util.Collection;
import static org.hamcrest.core.IsEqual.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ResourceLoader;
@@ -29,6 +32,7 @@ import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.data.annotation.Id;
import org.springframework.data.keyvalue.repository.KeyValueRepository;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.RedisKeyValueAdapter.EnableKeyspaceEvents;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
import org.springframework.data.repository.config.RepositoryConfiguration;
@@ -78,6 +82,25 @@ public class RedisRepositoryConfigurationExtensionUnitTests {
extension.getRepositoryConfigurations(configurationSource, loader, true));
}
/**
* @see DATAREDIS-491
*/
@Test
public void picksUpEnableKeyspaceEventsCorrectly() {
metadata = new StandardAnnotationMetadata(ConfigWithKeyspaceEventsDisabled.class, true);
configurationSource = new AnnotationRepositoryConfigurationSource(metadata, EnableRedisRepositories.class, loader,
environment);
RedisRepositoryConfigurationExtension extension = new RedisRepositoryConfigurationExtension();
BeanDefinitionRegistry beanDefintionRegistry = new SimpleBeanDefinitionRegistry();
extension.registerBeansForRoot(beanDefintionRegistry, configurationSource);
assertThat(beanDefintionRegistry.getBeanDefinition("redisKeyValueAdapter").getAttribute("enableKeyspaceEvents"),
equalTo((Object) EnableKeyspaceEvents.OFF));
}
private static void assertDoesNotHaveRepo(Class<?> repositoryInterface,
Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configs) {
@@ -108,6 +131,11 @@ public class RedisRepositoryConfigurationExtensionUnitTests {
}
@EnableRedisRepositories(considerNestedRepositories = true, enableKeyspaceEvents = EnableKeyspaceEvents.OFF)
static class ConfigWithKeyspaceEventsDisabled {
}
@RedisHash
static class Sample {
@Id String id;