Skip to content

Commit 0e17c9b

Browse files
committed
DATAREDIS-491 - Polishing.
Set EnableRedisRepositories.enableKeyspaceEvents default to OFF. Expiry notifications require to opt-in. Add JavaDoc. Add documentation for delayed/disabled event listener. Fix spelling. Use property value instead of attribute on RedisKeyValueAdapter bean definition. Remove trailing white spaces. Original pull request: #193.
1 parent 62c704d commit 0e17c9b

File tree

6 files changed

+119
-83
lines changed

6 files changed

+119
-83
lines changed

src/main/asciidoc/reference/redis-repositories.adoc

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class Person {
2626
====
2727

2828
We have a pretty simple domain object here. Note that it has a property named `id` annotated with `org.springframework.data.annotation.Id` and a `@RedisHash` annotation on its type.
29-
Those two are responsible for creating the actual key used to persist the hash.
29+
Those two are responsible for creating the actual key used to persist the hash.
3030

3131
NOTE: Properties annotated with `@Id` as well as those named `id` are considered as the identifier properties. Those with the annotation are favored over others.
3232

@@ -56,7 +56,7 @@ public class ApplicationConfig {
5656
public RedisConnectionFactory connectionFactory() {
5757
return new JedisConnectionFactory();
5858
}
59-
59+
6060
@Bean
6161
public RedisTemplate<?, ?> redisTemplate() {
6262
@@ -75,17 +75,17 @@ Given the setup above we can go on and inject `PersonRepository` into our compon
7575
----
7676
@Autowired PersonRepository repo;
7777
78-
public void basicCrudOperations() {
78+
public void basicCrudOperations() {
7979
8080
Person rand = new Person("rand", "al'thor");
8181
rand.setAddress(new Address("emond's field", "andor"));
82-
82+
8383
repo.save(rand); <1>
84-
84+
8585
repo.findOne(rand.getId()); <2>
86-
86+
8787
repo.count(); <3>
88-
88+
8989
repo.delete(rand); <4>
9090
}
9191
----
@@ -113,7 +113,7 @@ address.country = andor
113113
----
114114
<1> The `_class` attribute is included on root level as well as on any nested interface or abstract types.
115115
<2> Simple property values are mapped by path.
116-
<3> Properties of complex types are mapped by their dot path.
116+
<3> Properties of complex types are mapped by their dot path.
117117
====
118118

119119
[cols="1,2,3", options="header"]
@@ -163,7 +163,7 @@ Mapping behavior can be customized by registering the according `Converter` in `
163163
.Sample byte[] Converters
164164
====
165165
[source,java]
166-
----
166+
----
167167
@WritingConverter
168168
public class AddressToBytesConverter implements Converter<Address, byte[]> {
169169
@@ -183,15 +183,15 @@ public class AddressToBytesConverter implements Converter<Address, byte[]> {
183183
184184
@ReadingConverter
185185
public class BytesToAddressConverter implements Converter<byte[], Address> {
186-
186+
187187
private final Jackson2JsonRedisSerializer<Address> serializer;
188-
188+
189189
public BytesToAddressConverter() {
190-
190+
191191
serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);
192192
serializer.setObjectMapper(new ObjectMapper());
193193
}
194-
194+
195195
@Override
196196
public Address convert(byte[] value) {
197197
return serializer.deserialize(value);
@@ -203,10 +203,10 @@ public class BytesToAddressConverter implements Converter<byte[], Address> {
203203
Using the above byte[] `Converter` produces eg.
204204
====
205205
[source,text]
206-
----
206+
----
207207
_class = org.example.Person
208208
id = e2c7dcee-b8cd-4424-883e-736ce564363e
209-
firstname = rand
209+
firstname = rand
210210
lastname = al’thor
211211
address = { city : "emond's field", country : "andor" }
212212
----
@@ -216,7 +216,7 @@ address = { city : "emond's field", country : "andor" }
216216
.Sample Map<String,byte[]> Converters
217217
====
218218
[source,java]
219-
----
219+
----
220220
@WritingConverter
221221
public class AddressToMapConverter implements Converter<Address, Map<String,byte[]>> {
222222
@@ -228,7 +228,7 @@ public class AddressToMapConverter implements Converter<Address, Map<String,byte
228228
229229
@ReadingConverter
230230
public class MapToAddressConverter implements Converter<Address, Map<String, byte[]>> {
231-
231+
232232
@Override
233233
public Address convert(Map<String,byte[]> source) {
234234
return new Address(new String(source.get("ciudad")));
@@ -241,10 +241,10 @@ Using the above Map `Converter` produces eg.
241241

242242
====
243243
[source,text]
244-
----
244+
----
245245
_class = org.example.Person
246246
id = e2c7dcee-b8cd-4424-883e-736ce564363e
247-
firstname = rand
247+
firstname = rand
248248
lastname = al’thor
249249
ciudad = "emond's field"
250250
----
@@ -266,7 +266,7 @@ By default the prefix is set to `getClass().getName()`. This default can be alte
266266
public class ApplicationConfig {
267267
268268
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
269-
269+
270270
public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {
271271
272272
@Override
@@ -287,14 +287,14 @@ public class ApplicationConfig {
287287
public class ApplicationConfig {
288288
289289
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
290-
290+
291291
@Bean
292292
public RedisMappingContext keyValueMappingContext() {
293293
return new RedisMappingContext(
294294
new MappingConfiguration(
295-
new MyKeyspaceConfiguration(), new IndexConfiguration()));
295+
new MyKeyspaceConfiguration(), new IndexConfiguration()));
296296
}
297-
297+
298298
public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {
299299
300300
@Override
@@ -310,7 +310,7 @@ public class ApplicationConfig {
310310
== Secondary Indexes
311311
http://redis.io/topics/indexes[Secondary indexes] are used to enable lookup operations based on native Redis structures. Values are written to the according indexes on every save and are removed when objects are deleted or <<redis.repositories.expirations,expire>>.
312312

313-
Given the sample `Person` entity we can create an index for _firstname_ by annotating the property with `@Indexed`.
313+
Given the sample `Person` entity we can create an index for _firstname_ by annotating the property with `@Indexed`.
314314

315315
.Annotation driven indexing
316316
====
@@ -355,7 +355,7 @@ Further more the programmatic setup allows to define indexes on map keys and lis
355355
public class Person {
356356
357357
// ... other properties omitted
358-
358+
359359
Map<String,String> attributes; <1>
360360
Map<String Person> relatives; <2>
361361
List<Address> addresses; <3>
@@ -379,7 +379,7 @@ Same as with _keyspaces_ it is possible to configure indexes without the need of
379379
public class ApplicationConfig {
380380
381381
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
382-
382+
383383
public static class MyIndexConfiguration extends IndexConfiguration {
384384
385385
@Override
@@ -400,14 +400,14 @@ public class ApplicationConfig {
400400
public class ApplicationConfig {
401401
402402
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
403-
403+
404404
@Bean
405405
public RedisMappingContext keyValueMappingContext() {
406406
return new RedisMappingContext(
407407
new MappingConfiguration(
408-
new KeyspaceConfiguration(), new MyIndexConfiguration()));
408+
new KeyspaceConfiguration(), new MyIndexConfiguration()));
409409
}
410-
410+
411411
public static class MyIndexConfiguration extends IndexConfiguration {
412412
413413
@Override
@@ -427,24 +427,24 @@ The expiration time in seconds can be set via `@RedisHash(timeToLive=...)` as we
427427

428428
More flexible expiration times can be set by using the `@TimeToLive` annotation on either a numeric property or method. However do not apply `@TimeToLive` on both a method and a property within the same class.
429429

430-
.Expirations
430+
.Expirations
431431
====
432432
[source,java]
433433
----
434434
public class TimeToLiveOnProperty {
435435
436436
@Id
437437
private String id;
438-
438+
439439
@TimeToLive
440440
private Long expiration;
441441
}
442442
443443
public class TimeToLiveOnMethod {
444444
445-
@Id
445+
@Id
446446
private String id;
447-
447+
448448
@TimeToLive
449449
public long getTimeToLive() {
450450
return new Random().nextLong();
@@ -456,13 +456,19 @@ public class TimeToLiveOnMethod {
456456

457457
The repository implementation ensures subscription to http://redis.io/topics/notifications[Redis keyspace notifications] via `RedisMessageListenerContainer`.
458458

459-
When the expiration is set to a positive value the according `EXPIRE` command is executed.
459+
When the expiration is set to a positive value the according `EXPIRE` command is executed.
460460
Additionally to persisting the original, a _phantom_ copy is persisted in Redis and set to expire 5 minutes after the original one. This is done to enable the Repository support to publish `RedisKeyExpiredEvent` holding the expired value via Springs `ApplicationEventPublisher` whenever a key expires even though the original values have already been gone. Expiry events
461461
will be received on all connected applications using Spring Data Redis repositories.
462462

463-
The `RedisKeyExpiredEvent` will hold a copy of the actually expired domain object as well as the key.
463+
By default, the key expiry listener is disabled when initializing the application. The startup mode can be adjusted in `@EnableRedisRepositories` or `RedisKeyValueAdapter` to start the listener with the application or upon the first insert of an entity with a TTL. See `EnableKeyspaceEvents` for possible values.
464+
465+
The `RedisKeyExpiredEvent` will hold a copy of the actually expired domain object as well as the key.
466+
467+
NOTE: Delaying or disabling the expiry event listener startup impacts `RedisKeyExpiredEvent` publishing.
468+
A disabled event listener will not publish expiry events. A delayed startup can cause loss of events because the delayed
469+
listener initialization.
464470

465-
NOTE: The keyspace notification message listener will alter `notify-keyspace-events` settings in Redis if those are not already set. Existing settings will not be overridden, so it is left to the user to set those up correctly when not leaving them empty.
471+
NOTE: The keyspace notification message listener will alter `notify-keyspace-events` settings in Redis if those are not already set. Existing settings will not be overridden, so it is left to the user to set those up correctly when not leaving them empty. Please note that `CONFIG` is disabled on AWS ElastiCache and enabling the listener leads to an error.
466472

467473
NOTE: Redis Pub/Sub messages are not persistent. If a key expires while the application is down the expiry event will not be processed which may lead to secondary indexes containing still references to the expired object.
468474

@@ -474,29 +480,29 @@ On loading from Redis, references are resolved automatically and mapped back int
474480
.Sample Property Reference
475481
====
476482
[source,text]
477-
----
483+
----
478484
_class = org.example.Person
479485
id = e2c7dcee-b8cd-4424-883e-736ce564363e
480-
firstname = rand
486+
firstname = rand
481487
lastname = al’thor
482488
mother = persons:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56 <1>
483489
----
484490
<1> Reference stores the whole key (`keyspace:id`) of the referenced object.
485491
====
486492

487-
WARNING: Referenced Objects are not subject of persisting changes when saving the referencing object. Please make sure to persist changes on referenced objects separately, since only the reference will be stored.
493+
WARNING: Referenced Objects are not subject of persisting changes when saving the referencing object. Please make sure to persist changes on referenced objects separately, since only the reference will be stored.
488494
Indexes set on properties of referenced types will not be resolved.
489495

490496
[[redis.repositories.queries]]
491497
== Queries and Query Methods
492-
Query methods allow automatic derivation of simple finder queries from the method name.
498+
Query methods allow automatic derivation of simple finder queries from the method name.
493499

494500
.Sample Repository finder Method
495501
====
496502
[source,java]
497503
----
498504
public interface PersonRepository extends CrudRepository<Person, String> {
499-
505+
500506
List<Person> findByFirstname(String firstname);
501507
}
502508
----
@@ -552,7 +558,7 @@ Still some considerations have to be done as the default key distribution will s
552558
|
553559
|===============
554560
====
555-
561+
556562
Some commands like `SINTER` and `SUNION` can only be processed on the Server side when all involved keys map to the same slot. Otherwise computation has to be done on client side.
557563
Therefore it be useful to pin keyspaces to a single slot which allows to make use of Redis serverside computation right away.
558564

src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,11 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter
9898
private RedisOperations<?, ?> redisOps;
9999
private RedisConverter converter;
100100
private RedisMessageListenerContainer messageListenerContainer;
101-
private AtomicReference<KeyExpirationEventMessageListener> expirationListener = new AtomicReference<KeyExpirationEventMessageListener>(
101+
private final AtomicReference<KeyExpirationEventMessageListener> expirationListener = new AtomicReference<KeyExpirationEventMessageListener>(
102102
null);
103103
private ApplicationEventPublisher eventPublisher;
104104

105-
private EnableKeyspaceEvents enableKeyspaceEvents = EnableKeyspaceEvents.ON_STARTUP;
105+
private EnableKeyspaceEvents enableKeyspaceEvents = EnableKeyspaceEvents.OFF;
106106

107107
/**
108108
* Creates new {@link RedisKeyValueAdapter} with default {@link RedisMappingContext} and default
@@ -144,10 +144,9 @@ public RedisKeyValueAdapter(RedisOperations<?, ?> redisOps, RedisMappingContext
144144
mappingConverter.setCustomConversions(customConversions == null ? new CustomConversions() : customConversions);
145145
mappingConverter.afterPropertiesSet();
146146

147-
converter = mappingConverter;
147+
this.converter = mappingConverter;
148148
this.redisOps = redisOps;
149-
150-
intiMessageListenerContainer();
149+
initMessageListenerContainer();
151150
}
152151

153152
/**
@@ -162,10 +161,9 @@ public RedisKeyValueAdapter(RedisOperations<?, ?> redisOps, RedisConverter redis
162161

163162
Assert.notNull(redisOps, "RedisOperations must not be null!");
164163

165-
converter = redisConverter;
164+
this.converter = redisConverter;
166165
this.redisOps = redisOps;
167-
168-
intiMessageListenerContainer();
166+
initMessageListenerContainer();
169167
}
170168

171169
/**
@@ -493,7 +491,9 @@ public void destroy() throws Exception {
493491
this.expirationListener.get().destroy();
494492
}
495493

496-
this.messageListenerContainer.destroy();
494+
if(this.messageListenerContainer != null){
495+
this.messageListenerContainer.destroy();
496+
}
497497
}
498498

499499
/*
@@ -535,12 +535,12 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
535535
this.eventPublisher = applicationContext;
536536
}
537537

538-
private void intiMessageListenerContainer() {
538+
private void initMessageListenerContainer() {
539539

540540
this.messageListenerContainer = new RedisMessageListenerContainer();
541-
messageListenerContainer.setConnectionFactory(((RedisTemplate<?, ?>) redisOps).getConnectionFactory());
542-
messageListenerContainer.afterPropertiesSet();
543-
messageListenerContainer.start();
541+
this.messageListenerContainer.setConnectionFactory(((RedisTemplate<?, ?>) redisOps).getConnectionFactory());
542+
this.messageListenerContainer.afterPropertiesSet();
543+
this.messageListenerContainer.start();
544544
}
545545

546546
private void initKeyExpirationListener() {

0 commit comments

Comments
 (0)