Skip to content

Commit 597e76f

Browse files
DATAREDIS-481 - Hacking: API refinements & additional tests.
1 parent f9e85c6 commit 597e76f

File tree

6 files changed

+509
-51
lines changed

6 files changed

+509
-51
lines changed

src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class DefaultRedisCacheWriter implements RedisCacheWriter {
3737
.getBytes(Charset.forName("UTF-8"));
3838

3939
private final RedisConnectionFactory connectionFactory;
40-
private final Duration lockTimeout;
40+
private final Duration sleepTime;
4141

4242
/**
4343
* @param connectionFactory must not be {@literal null}.
@@ -48,20 +48,34 @@ class DefaultRedisCacheWriter implements RedisCacheWriter {
4848

4949
/**
5050
* @param connectionFactory must not be {@literal null}.
51-
* @param lockTimeout must not be {@literal null}. Use {@link Duration#ZERO} to disable locking.
51+
* @param sleepTime sleep time between lock request attempts. Must not be {@literal null}. Use {@link Duration#ZERO}
52+
* to disable locking.
5253
*/
53-
DefaultRedisCacheWriter(RedisConnectionFactory connectionFactory, Duration lockTimeout) {
54+
DefaultRedisCacheWriter(RedisConnectionFactory connectionFactory, Duration sleepTime) {
5455

5556
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
56-
Assert.notNull(lockTimeout, "LockTimeout must not be null!");
57+
Assert.notNull(sleepTime, "SleepTime must not be null!");
5758

5859
this.connectionFactory = connectionFactory;
59-
this.lockTimeout = lockTimeout;
60+
this.sleepTime = sleepTime;
61+
}
62+
63+
public static DefaultRedisCacheWriter nonLockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {
64+
return new DefaultRedisCacheWriter(connectionFactory);
65+
}
66+
67+
public static DefaultRedisCacheWriter lockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {
68+
return new DefaultRedisCacheWriter(connectionFactory, Duration.ofMillis(50));
6069
}
6170

6271
@Override
6372
public void put(String name, byte[] key, byte[] value, Duration ttl) {
6473

74+
Assert.notNull(name, "Name must not be null!");
75+
Assert.notNull(key, "Key must not be null!");
76+
Assert.notNull(value, "Value must not be null!");
77+
Assert.notNull(ttl, "Ttl must not be null!");
78+
6579
execute(name, connection -> {
6680

6781
if (shouldExpireWithin(ttl)) {
@@ -76,12 +90,21 @@ public void put(String name, byte[] key, byte[] value, Duration ttl) {
7690

7791
@Override
7892
public byte[] get(String name, byte[] key) {
93+
94+
Assert.notNull(name, "Name must not be null!");
95+
Assert.notNull(key, "Key must not be null!");
96+
7997
return execute(name, connection -> connection.get(key));
8098
}
8199

82100
@Override
83101
public byte[] putIfAbsent(String name, byte[] key, byte[] value, Duration ttl) {
84102

103+
Assert.notNull(name, "Name must not be null!");
104+
Assert.notNull(key, "Key must not be null!");
105+
Assert.notNull(value, "Value must not be null!");
106+
Assert.notNull(ttl, "Ttl must not be null!");
107+
85108
return execute(name, connection -> {
86109

87110
if (connection.setNX(key, value)) {
@@ -98,11 +121,15 @@ public byte[] putIfAbsent(String name, byte[] key, byte[] value, Duration ttl) {
98121

99122
@Override
100123
public void remove(String name, byte[] key) {
124+
125+
Assert.notNull(name, "Name must not be null!");
126+
Assert.notNull(key, "Key must not be null!");
127+
101128
execute(name, connection -> connection.del(key));
102129
}
103130

104131
public void lock(String name) {
105-
executeWithoutLockCheck(connection -> doLock(name, connection));
132+
execute(name, connection -> doLock(name, connection));
106133
}
107134

108135
private Boolean doLock(String name, RedisConnection connection) {
@@ -128,35 +155,40 @@ private boolean doCheckLock(String name, RedisConnection connection) {
128155
@Override
129156
public void clean(String name, byte[] pattern) {
130157

131-
RedisConnection connection = connectionFactory.getConnection();
158+
Assert.notNull(name, "Name must not be null!");
159+
Assert.notNull(pattern, "Pattern must not be null!");
132160

133-
if (isLockingCacheWriter()) {
134-
doLock(name, connection);
135-
}
136-
137-
try {
138-
if (connection instanceof RedisClusterConnection) {
161+
execute(name, connection -> {
139162

140-
byte[][] keys = connection.keys(pattern).stream().toArray(size -> new byte[size][]);
141-
connection.del(keys);
142-
} else {
143-
connection.eval(CLEAN_SCRIPT, ReturnType.INTEGER, 0, pattern);
163+
if (isLockingCacheWriter()) {
164+
doLock(name, connection);
144165
}
145-
} finally {
146166

147-
if (isLockingCacheWriter()) {
148-
doUnlock(name, connection);
167+
try {
168+
if (connection instanceof RedisClusterConnection) {
169+
170+
byte[][] keys = connection.keys(pattern).stream().toArray(size -> new byte[size][]);
171+
connection.del(keys);
172+
} else {
173+
connection.eval(CLEAN_SCRIPT, ReturnType.INTEGER, 0, pattern);
174+
}
175+
} finally {
176+
177+
if (isLockingCacheWriter()) {
178+
doUnlock(name, connection);
179+
}
149180
}
150-
connection.close();
151-
}
181+
182+
return "OK";
183+
});
152184
}
153185

154186
public <T> T execute(String name, ConnectionCallback<T> callback) {
155187

156188
RedisConnection connection = connectionFactory.getConnection();
157189
try {
158190

159-
checkAndPotentiallyWaitForLock(name, connection);
191+
checkAndPotentiallyWaitUntilUnlocked(name, connection);
160192
return callback.doWithConnection(connection);
161193
} finally {
162194
connection.close();
@@ -175,14 +207,14 @@ private <T> T executeWithoutLockCheck(ConnectionCallback<T> callback) {
175207
}
176208

177209
public boolean isLockingCacheWriter() {
178-
return !lockTimeout.isZero() && !lockTimeout.isNegative();
210+
return !sleepTime.isZero() && !sleepTime.isNegative();
179211
}
180212

181-
private void checkAndPotentiallyWaitForLock(String name, RedisConnection connection) {
213+
private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection connection) {
182214

183215
if (isLockingCacheWriter()) {
184216

185-
long timeout = lockTimeout.toMillis();
217+
long timeout = sleepTime.toMillis();
186218

187219
while (doCheckLock(name, connection)) {
188220
try {

src/main/java/org/springframework/data/redis/cache/NRedisCacheManager.java

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,46 +7,71 @@
77
import java.util.Map;
88

99
import org.springframework.cache.Cache;
10-
import org.springframework.cache.support.AbstractCacheManager;
10+
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
11+
import org.springframework.data.redis.connection.RedisConnectionFactory;
1112
import org.springframework.util.Assert;
1213

1314
/**
1415
* @author Christoph Strobl
1516
* @since 2017/05
1617
*/
17-
public class NRedisCacheManager extends AbstractCacheManager {
18+
public class NRedisCacheManager extends AbstractTransactionSupportingCacheManager {
1819

19-
final DefaultRedisCacheWriter cacheWriter;
20-
final RedisCacheConfiguration defaultCacheOptions;
20+
final RedisCacheWriter cacheWriter;
21+
final RedisCacheConfiguration defaultCacheConfig;
2122
final Map<String, RedisCacheConfiguration> intialCacheConfiguration;
2223

23-
public NRedisCacheManager(DefaultRedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
24-
String... initialCacheNames) {
24+
public NRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
25+
String... initialCacheNames) {
2526

2627
Assert.notNull(cacheWriter, "CacheWriter must not be null!");
2728
Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!");
2829

2930
this.cacheWriter = cacheWriter;
30-
this.defaultCacheOptions = defaultCacheConfiguration;
31+
this.defaultCacheConfig = defaultCacheConfiguration;
3132
this.intialCacheConfiguration = new LinkedHashMap<>(initialCacheNames.length, 1);
3233

3334
for (String cacheName : initialCacheNames) {
3435
this.intialCacheConfiguration.put(cacheName, defaultCacheConfiguration);
3536
}
3637
}
3738

38-
public NRedisCacheManager(DefaultRedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
39-
Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
39+
public NRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
40+
Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
4041

4142
Assert.notNull(cacheWriter, "CacheWriter must not be null!");
4243
Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!");
4344
Assert.notNull(initialCacheConfigurations, "InitialCacheConfigurations must not be null!");
4445

4546
this.cacheWriter = cacheWriter;
46-
this.defaultCacheOptions = defaultCacheConfiguration;
47+
this.defaultCacheConfig = defaultCacheConfiguration;
4748
this.intialCacheConfiguration = new LinkedHashMap<>(initialCacheConfigurations);
4849
}
4950

51+
/**
52+
* Create a new {@link NRedisCacheManager} with defaults applied.
53+
* <dl>
54+
* <dt>locking</dt>
55+
* <dd>disabled</dd>
56+
* <dt>cache configuration</dt>
57+
* <dd>{@link RedisCacheConfiguration#defaultCacheConfig()}</dd>
58+
* <dt>initial caches</dt>
59+
* <dd>none</dd>
60+
* <dt>transaction aware</dt>
61+
* <dd>no</dd>
62+
* </dl>
63+
*
64+
* @param connectionFactory must not be {@literal null}.
65+
* @return
66+
*/
67+
public static NRedisCacheManager iAmFineWithTheDefaults(RedisConnectionFactory connectionFactory) {
68+
69+
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
70+
71+
return new NRedisCacheManager(new DefaultRedisCacheWriter(connectionFactory),
72+
RedisCacheConfiguration.defaultCacheConfig());
73+
}
74+
5075
@Override
5176
protected Collection<NRedisCache> loadCaches() {
5277

@@ -59,10 +84,10 @@ protected Collection<NRedisCache> loadCaches() {
5984

6085
@Override
6186
protected Cache getMissingCache(String name) {
62-
return createRedisCache(name, defaultCacheOptions);
87+
return createRedisCache(name, defaultCacheConfig);
6388
}
6489

6590
NRedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
66-
return new NRedisCache(name, cacheWriter, cacheConfig);
91+
return new NRedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
6792
}
6893
}

src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ private RedisCacheConfiguration(Duration ttl, Boolean cacheNullValues, Boolean u
5656
/**
5757
* Default {@link RedisCacheConfiguration} using the following:
5858
* <dl>
59-
* <dt>entry timeout</dt>
60-
* <dd>0 ms</dd>
59+
* <dt>key expiration</dt>
60+
* <dd>eternal</dd>
6161
* <dt>cache null values</dt>
6262
* <dd>yes</dd>
6363
* <dt>prefix cache keys</dt>
@@ -85,6 +85,8 @@ public static RedisCacheConfiguration defaultCacheConfig() {
8585
* @return new {@link RedisCacheConfiguration}.
8686
*/
8787
public RedisCacheConfiguration entryTimeout(Duration timeout) {
88+
89+
Assert.notNull(timeout, "Timeout must not be null!");
8890
return new RedisCacheConfiguration(timeout, cacheNullValues, usePrefix, defaultPrefix, keySerializationPair,
8991
valueSerializationPair);
9092
}
@@ -165,15 +167,15 @@ boolean getAllowCacheNullValues() {
165167
return cacheNullValues != null ? cacheNullValues.booleanValue() : true;
166168
}
167169

168-
public SerializationPair<String> getKeySerializationPair() {
170+
SerializationPair<String> getKeySerializationPair() {
169171
return keySerializationPair;
170172
}
171173

172-
public SerializationPair getValueSerializationPair() {
174+
SerializationPair getValueSerializationPair() {
173175
return valueSerializationPair;
174176
}
175177

176-
public Duration getTimeout() {
178+
Duration getTimeout() {
177179
return timeout != null ? timeout : Duration.ZERO;
178180
}
179181

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.cache;
17+
18+
import lombok.RequiredArgsConstructor;
19+
import lombok.experimental.Delegate;
20+
21+
import java.util.Arrays;
22+
import java.util.Collection;
23+
import java.util.stream.Collectors;
24+
25+
import org.springframework.data.redis.SettingsUtils;
26+
import org.springframework.data.redis.connection.RedisConnectionFactory;
27+
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
28+
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
29+
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
30+
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
31+
import org.springframework.data.redis.serializer.OxmSerializer;
32+
import org.springframework.data.redis.serializer.RedisSerializer;
33+
import org.springframework.oxm.xstream.XStreamMarshaller;
34+
35+
/**
36+
* @author Christoph Strobl
37+
*/
38+
class CacheTestParams {
39+
40+
private static Collection<RedisConnectionFactory> connectionFactories() {
41+
42+
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
43+
jedisConnectionFactory.setPort(SettingsUtils.getPort());
44+
jedisConnectionFactory.setHostName(SettingsUtils.getHost());
45+
jedisConnectionFactory.afterPropertiesSet();
46+
47+
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory();
48+
lettuceConnectionFactory.setPort(SettingsUtils.getPort());
49+
lettuceConnectionFactory.setHostName(SettingsUtils.getHost());
50+
lettuceConnectionFactory.afterPropertiesSet();
51+
52+
return Arrays.asList(new FixDamnedJunitParameterizedNameForConnectionFactory(jedisConnectionFactory),
53+
new FixDamnedJunitParameterizedNameForConnectionFactory(lettuceConnectionFactory));
54+
}
55+
56+
static Collection<Object[]> justConnectionFactories() {
57+
return connectionFactories().stream().map(factory -> new Object[] { factory }).collect(Collectors.toList());
58+
}
59+
60+
static Collection<Object[]> connectionFactoriesAndSerializers() {
61+
62+
// XStream serializer
63+
XStreamMarshaller xstream = new XStreamMarshaller();
64+
xstream.afterPropertiesSet();
65+
66+
OxmSerializer oxmSerializer = new OxmSerializer(xstream, xstream);
67+
GenericJackson2JsonRedisSerializer jackson2Serializer = new GenericJackson2JsonRedisSerializer();
68+
JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();
69+
70+
return connectionFactories()
71+
.stream().flatMap(factory -> Arrays
72+
.asList( //
73+
new Object[] { factory, new FixDamnedJunitParameterizedNameForRedisSerializer(jdkSerializer) }, //
74+
new Object[] { factory, new FixDamnedJunitParameterizedNameForRedisSerializer(jackson2Serializer) }, //
75+
new Object[] { factory, new FixDamnedJunitParameterizedNameForRedisSerializer(oxmSerializer) })
76+
.stream())
77+
.collect(Collectors.toList());
78+
}
79+
80+
@RequiredArgsConstructor
81+
static class FixDamnedJunitParameterizedNameForConnectionFactory/* ¯\_(ツ)_/¯ */ implements RedisConnectionFactory {
82+
83+
final @Delegate RedisConnectionFactory connectionFactory;
84+
85+
@Override // Why Junit? Why?
86+
public String toString() {
87+
return connectionFactory.getClass().getSimpleName();
88+
}
89+
}
90+
91+
@RequiredArgsConstructor
92+
static class FixDamnedJunitParameterizedNameForRedisSerializer/* ¯\_(ツ)_/¯ */ implements RedisSerializer {
93+
94+
final @Delegate RedisSerializer serializer;
95+
96+
@Override // Why Junit? Why?
97+
public String toString() {
98+
return serializer.getClass().getSimpleName();
99+
}
100+
}
101+
}

0 commit comments

Comments
 (0)