DATAREDIS-72

+ add prefix strategy
This commit is contained in:
Costin Leau
2011-07-15 19:11:35 +03:00
parent 63b1e862b7
commit 0691af7ca4
6 changed files with 121 additions and 38 deletions

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2011 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.cache;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Default implementation for {@link RedisCachePrefix} which uses the given cache name and a delimiter for creating the prefix.
*
* @author Costin Leau
*/
public class DefaultRedisCachePrefix implements RedisCachePrefix {
private final RedisSerializer serializer = new StringRedisSerializer();
private final String delimiter;
public DefaultRedisCachePrefix() {
this(":");
}
public DefaultRedisCachePrefix(String delimiter) {
this.delimiter = delimiter;
}
@Override
public byte[] prefix(String cacheName) {
return serializer.serialize((delimiter != null ? cacheName.concat(":") : cacheName));
}
}

View File

@@ -23,13 +23,9 @@ import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.DefaultValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.support.collections.DefaultRedisSet;
import org.springframework.data.redis.support.collections.RedisSet;
import org.springframework.util.Assert;
/**
@@ -38,12 +34,11 @@ import org.springframework.util.Assert;
* @author Costin Leau
*/
@SuppressWarnings("unchecked")
public class RedisCache implements Cache {
class RedisCache implements Cache {
private final String name;
private final RedisSet<Object> keys;
private final RedisTemplate template;
private final byte[] nameAsBytes;
private final byte[] prefix;
private final byte[] setName;
/**
@@ -51,30 +46,20 @@ public class RedisCache implements Cache {
* Constructs a new <code>RedisCache</code> instance.
*
* @param name cache name
* @param prefix
* @param cachePrefix
*/
public RedisCache(String name, RedisTemplate<? extends Object, ? extends Object> template) {
RedisCache(String name, byte[] prefix, RedisTemplate<? extends Object, ? extends Object> template) {
Assert.hasText(name, "non-empty cache name is required");
this.name = name;
this.template = template;
this.prefix = prefix;
StringRedisSerializer stringSerializer = new StringRedisSerializer();
this.nameAsBytes = stringSerializer.serialize(name + ":");
// extract connection and create an ops for set
RedisConnectionFactory connectionFactory = template.getConnectionFactory();
RedisTemplate<String, Object> setTemplate = new RedisTemplate<String, Object>();
setTemplate.setConnectionFactory(connectionFactory);
setTemplate.setDefaultSerializer(template.getDefaultSerializer());
setTemplate.setKeySerializer(stringSerializer);
// the values of the set are the cache keys
setTemplate.setValueSerializer(template.getKeySerializer());
setTemplate.afterPropertiesSet();
String sName = name + "-keys";
this.keys = new DefaultRedisSet<Object>(sName, setTemplate);
// name of the set holding the keys
String sName = name + "~keys";
this.setName = stringSerializer.serialize(sName);
}
@@ -108,7 +93,7 @@ public class RedisCache implements Cache {
@Override
public void put(final Object key, final Object value) {
final byte[] k = computeKey(key);
template.execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
connection.set(k, template.getValueSerializer().serialize(value));
@@ -135,6 +120,7 @@ public class RedisCache implements Cache {
@Override
public void clear() {
// need to del each key individually
// TODO: change this to a sorted set to allow pagination
template.execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
// need to paginate the keys
@@ -149,11 +135,13 @@ public class RedisCache implements Cache {
}
private byte[] computeKey(Object key) {
RedisSerializer keySerializer = template.getKeySerializer();
byte[] k = keySerializer.serialize(key);
byte[] k = template.getKeySerializer().serialize(key);
byte[] result = Arrays.copyOf(nameAsBytes, nameAsBytes.length + k.length);
System.arraycopy(k, 0, result, nameAsBytes.length, k.length);
if (prefix == null || prefix.length == 0)
return k;
byte[] result = Arrays.copyOf(prefix, prefix.length + k.length);
System.arraycopy(k, 0, result, prefix.length, k.length);
return result;
}
}

View File

@@ -27,6 +27,8 @@ import org.springframework.data.redis.core.RedisTemplate;
/**
* CacheManager implementation for Redis.
* By default saves the keys by appending a prefix (which acts as a namespace). For performance reasons, the current implementation
* uses a set for the keys in each cache.
*
* @author Costin Leau
*/
@@ -37,6 +39,9 @@ public class RedisCacheManager implements CacheManager {
private final Collection<String> names = Collections.unmodifiableSet(caches.keySet());
private final RedisTemplate template;
private boolean usePrefix;
private RedisCachePrefix cachePrefix = new DefaultRedisCachePrefix();
public RedisCacheManager(RedisTemplate template) {
this.template = template;
}
@@ -44,7 +49,7 @@ public class RedisCacheManager implements CacheManager {
public Cache getCache(String name) {
Cache c = caches.get(name);
if (c == null) {
c = new RedisCache(name, template);
c = new RedisCache(name, (usePrefix ? cachePrefix.prefix(name) : null), template);
caches.put(name, c);
names.add(name);
}
@@ -55,4 +60,13 @@ public class RedisCacheManager implements CacheManager {
public Collection<String> getCacheNames() {
return names;
}
/**
* Sets the cachePrefix.
*
* @param cachePrefix the cachePrefix to set
*/
public void setCachePrefix(RedisCachePrefix cachePrefix) {
this.cachePrefix = cachePrefix;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2011 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.cache;
/**
* Contract for generating 'prefixes' for Cache keys saved in Redis.
* Due to the 'flat' nature of the Redis storage, the prefix is used as a 'namespace' for grouping the key/values inside a cache (and
* to avoid collision with other caches or keys inside Redis).
*
* @author Costin Leau
*/
public interface RedisCachePrefix {
/**
* Returns the prefix for the given cache (identified by name).
* Note the prefix is returned in raw form so it can be saved directly to Redis without any serialization.
*
* @param cacheName the name of the cache using the prefix
* @return the prefix for the given cache.
*/
byte[] prefix(String cacheName);
}