diff --git a/build/context/admin/cdriver/Redis.cfc b/build/context/admin/cdriver/Redis.cfc index 4457983..0761cb5 100644 --- a/build/context/admin/cdriver/Redis.cfc +++ b/build/context/admin/cdriver/Redis.cfc @@ -14,6 +14,13 @@ component extends="Cache" { description = "Port Redis is listening on.", type = "text" ) + ,field( + displayName = "Namespace", + name = "namespace", + defaultValue = "lucee:cache", + required = false, + description = "Keys namespace. If using the same Redis instance for more than one cache use a unique namespace to avoid keys names clashing." + ) ,group("Authentication","Authentication Credentials if necessary.") ,field(displayName = "Password", diff --git a/build/context/admin/cdriver/RedisSentinel.cfc b/build/context/admin/cdriver/RedisSentinel.cfc index a13d144..2197886 100644 --- a/build/context/admin/cdriver/RedisSentinel.cfc +++ b/build/context/admin/cdriver/RedisSentinel.cfc @@ -18,8 +18,8 @@ component extends="Cache" { displayName = "Namespace", name = "namespace", defaultValue = "lucee:cache", - required = true, - description = "Keys namespace. Be sure that any cache use a unique namespace to avoid keys names clashing." + required = false, + description = "Keys namespace. If using the same Redis instance for more than one cache use a unique namespace to avoid keys names clashing." ) ,group("Authentication","Authentication Credentials if necessary.") diff --git a/source/java/src/lucee/extension/io/cache/redis/AbstractRedisCache.java b/source/java/src/lucee/extension/io/cache/redis/AbstractRedisCache.java index 312ccc0..54eb27c 100644 --- a/source/java/src/lucee/extension/io/cache/redis/AbstractRedisCache.java +++ b/source/java/src/lucee/extension/io/cache/redis/AbstractRedisCache.java @@ -36,6 +36,7 @@ public abstract class AbstractRedisCache extends CacheSupport { private int maxIdle; private int minIdle; private int defaultExpire; + private String namespace; public void init(Config config, String[] cacheName, Struct[] arguments) { // Not used at the moment @@ -49,6 +50,9 @@ public void init(Struct arguments) throws IOException { defaultExpire = caster.toIntValue(arguments.get("timeToLiveSeconds", null), 0); + namespace = caster.toString(arguments.get("namespace", null), null); + if (Util.isEmpty(namespace)) namespace = null; + // for config maxTotal = caster.toIntValue(arguments.get("maxTotal", null), 0); maxIdle = caster.toIntValue(arguments.get("maxIdle", null), 0); @@ -76,11 +80,11 @@ public CacheEntry getCacheEntry(String key) throws IOException { key = validateKey(key); String val = null; try { - val = conn.get(key); + val = conn.get(RedisCacheUtils.addNamespace(namespace, key)); } catch (JedisDataException jde) { String msg = jde.getMessage() + ""; - if (msg.startsWith("WRONGTYPE")) val = conn.lpop(key); + if (msg.startsWith("WRONGTYPE")) val = conn.lpop(RedisCacheUtils.addNamespace(namespace, key)); } if (val == null) throw new IOException("Cache key [" + key + "] does not exists"); return new RedisCacheEntry(this, key, evaluate(val), val.length()); @@ -112,23 +116,31 @@ public void put(String key, Object val, Long idle, Long expire) { // fields.put("value", value); // fields.put("hitCount", "0"); // conn.hmset(key, fields); - conn.set(validateKey(key), serialize(val)); + // TODO different to default? // System.err.println("idle:" + idle); // System.err.println("expire:" + expire); // System.err.println("defaultExpire:" + defaultExpire); - int ex = 0; + int ex = defaultExpire; + if (expire != null) { - ex = (int) (expire / 1000); + ex = (int) (expire / 1000); } - else { - ex = defaultExpire; + else if (idle != null) { + // note: if this cache is being used as a session store + // then idle will be passed in as -1 when a new session + // is created and first stored. Avoid setting `ex` in + // this case so we don't get a cache item without a TTL + // when the cache has a default TTL + if (idle >= 0) { + ex = (int) (idle / 1000); + } } - if (ex > 0) { - // System.err.println(key + ":" + ex); - conn.expire(validateKey(key), ex); + conn.setex(RedisCacheUtils.addNamespace(namespace, validateKey(key)), ex, serialize(val)); + } else { + conn.set(RedisCacheUtils.addNamespace(namespace, validateKey(key)), serialize(val)); } } catch (PageException e) { @@ -143,7 +155,7 @@ public void put(String key, Object val, Long idle, Long expire) { public boolean contains(String key) { Jedis conn = jedisSilent(); try { - return conn.exists(validateKey(key)); + return conn.exists(RedisCacheUtils.addNamespace(namespace, validateKey(key))); } finally { RedisCacheUtils.close(conn); @@ -154,7 +166,7 @@ public boolean contains(String key) { public boolean remove(String key) throws IOException { Jedis conn = jedis(); try { - return conn.del(validateKey(key)) > 0; + return conn.del(RedisCacheUtils.addNamespace(namespace, validateKey(key))) > 0; } finally { RedisCacheUtils.close(conn); @@ -165,7 +177,7 @@ public boolean remove(String key) throws IOException { public List keys() throws IOException { Jedis conn = jedis(); try { - return toList(conn.keys("*")); + return toList(conn.keys(RedisCacheUtils.addNamespace(namespace, "*"))); } finally { RedisCacheUtils.close(conn); @@ -208,7 +220,7 @@ private List toList(Set keys) throws IOException { List list = new ArrayList(); Iterator it = keys.iterator(); while (it.hasNext()) { - list.add(it.next()); + list.add(RedisCacheUtils.removeNamespace(namespace, it.next())); } return list; } @@ -227,7 +239,7 @@ public CacheEntry getQuiet(String key, CacheEntry defaultValue) { @Override public int clear() throws IOException { Jedis conn = jedis(); - Set set = conn.keys("*"); + Set set = conn.keys(RedisCacheUtils.addNamespace(namespace, "*")); String[] keys = engine.getListUtil().toStringArray(set); if (keys.length == 0) return 0; return engine.getCastUtil().toIntValue(conn.del(keys), 0); diff --git a/source/java/src/lucee/extension/io/cache/redis/RedisCacheUtils.java b/source/java/src/lucee/extension/io/cache/redis/RedisCacheUtils.java index 88e55c8..5ee5342 100644 --- a/source/java/src/lucee/extension/io/cache/redis/RedisCacheUtils.java +++ b/source/java/src/lucee/extension/io/cache/redis/RedisCacheUtils.java @@ -11,19 +11,19 @@ public class RedisCacheUtils { private RedisCacheUtils() {} public static String removeNamespace(String nameSpace, String key) { - if (nameSpace != null && key.startsWith(nameSpace)) return key.replace(nameSpace + ":", ""); + if (nameSpace != null && key.startsWith(nameSpace.toLowerCase())) return key.replace(nameSpace.toLowerCase() + ":", ""); return key; } - public static String addNamespace(String nameSpace, String key) throws IOException { // TODO rethink this - if (nameSpace == null) return key.toLowerCase(); + public static String addNamespace(String nameSpace, String key) { + if (nameSpace == null) return key; // key is already lowercased due to validateKey call - if (key.startsWith(nameSpace + ":")) { + if (key.startsWith(nameSpace.toLowerCase() + ":")) { return key; } - String res = nameSpace + ':' + key; - return res.toLowerCase();// setting case sensitive + String res = nameSpace.toLowerCase() + ':' + key; // setting case sensitive + return res; } public static void close(Jedis jedis) {