Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit 39b2b3d

Browse files
author
Dan Richelson
committed
RedisFeatureStore: Improve pool resource management.
1 parent a2794b9 commit 39b2b3d

File tree

1 file changed

+46
-40
lines changed

1 file changed

+46
-40
lines changed

src/main/java/com/launchdarkly/client/RedisFeatureStore.java

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
/**
3131
* A thread-safe, versioned store for {@link FeatureFlag} objects backed by Redis. Also
3232
* supports an optional in-memory cache configuration that can be used to improve performance.
33-
*
3433
*/
3534
public class RedisFeatureStore implements FeatureStore {
3635
private static final Logger logger = LoggerFactory.getLogger(RedisFeatureStore.class);
@@ -47,9 +46,9 @@ public class RedisFeatureStore implements FeatureStore {
4746
* Creates a new store instance that connects to Redis with the provided host, port, prefix, and cache timeout. Uses a default
4847
* connection pool configuration.
4948
*
50-
* @param host the host for the Redis connection
51-
* @param port the port for the Redis connection
52-
* @param prefix a namespace prefix for all keys stored in Redis
49+
* @param host the host for the Redis connection
50+
* @param port the port for the Redis connection
51+
* @param prefix a namespace prefix for all keys stored in Redis
5352
* @param cacheTimeSecs an optional timeout for the in-memory cache. If set to 0, no in-memory caching will be performed
5453
* @deprecated as of 1.1. Please use the {@link RedisFeatureStoreBuilder#build()} for a more flexible way of constructing a {@link RedisFeatureStore}.
5554
*/
@@ -62,8 +61,8 @@ public RedisFeatureStore(String host, int port, String prefix, long cacheTimeSec
6261
* Creates a new store instance that connects to Redis with the provided URI, prefix, and cache timeout. Uses a default
6362
* connection pool configuration.
6463
*
65-
* @param uri the URI for the Redis connection
66-
* @param prefix a namespace prefix for all keys stored in Redis
64+
* @param uri the URI for the Redis connection
65+
* @param prefix a namespace prefix for all keys stored in Redis
6766
* @param cacheTimeSecs an optional timeout for the in-memory cache. If set to 0, no in-memory caching will be performed
6867
* @deprecated as of 1.1. Please use the {@link RedisFeatureStoreBuilder#build()} for a more flexible way of constructing a {@link RedisFeatureStore}.
6968
*/
@@ -75,11 +74,11 @@ public RedisFeatureStore(URI uri, String prefix, long cacheTimeSecs) {
7574
/**
7675
* Creates a new store instance that connects to Redis with the provided host, port, prefix, cache timeout, and connection pool settings.
7776
*
78-
* @param host the host for the Redis connection
79-
* @param port the port for the Redis connection
80-
* @param prefix a namespace prefix for all keys stored in Redis
77+
* @param host the host for the Redis connection
78+
* @param port the port for the Redis connection
79+
* @param prefix a namespace prefix for all keys stored in Redis
8180
* @param cacheTimeSecs an optional timeout for the in-memory cache. If set to 0, no in-memory caching will be performed
82-
* @param poolConfig an optional pool config for the Jedis connection pool
81+
* @param poolConfig an optional pool config for the Jedis connection pool
8382
* @deprecated as of 1.1. Please use the {@link RedisFeatureStoreBuilder#build()} for a more flexible way of constructing a {@link RedisFeatureStore}.
8483
*/
8584
@Deprecated
@@ -93,10 +92,10 @@ public RedisFeatureStore(String host, int port, String prefix, long cacheTimeSec
9392
/**
9493
* Creates a new store instance that connects to Redis with the provided URI, prefix, cache timeout, and connection pool settings.
9594
*
96-
* @param uri the URI for the Redis connection
97-
* @param prefix a namespace prefix for all keys stored in Redis
95+
* @param uri the URI for the Redis connection
96+
* @param prefix a namespace prefix for all keys stored in Redis
9897
* @param cacheTimeSecs an optional timeout for the in-memory cache. If set to 0, no in-memory caching will be performed
99-
* @param poolConfig an optional pool config for the Jedis connection pool
98+
* @param poolConfig an optional pool config for the Jedis connection pool
10099
* @deprecated as of 1.1. Please use the {@link RedisFeatureStoreBuilder#build()} for a more flexible way of constructing a {@link RedisFeatureStore}.
101100
*/
102101
@Deprecated
@@ -109,7 +108,7 @@ public RedisFeatureStore(URI uri, String prefix, long cacheTimeSecs, JedisPoolCo
109108

110109
/**
111110
* Creates a new store instance that connects to Redis based on the provided {@link RedisFeatureStoreBuilder}.
112-
*
111+
* <p>
113112
* See the {@link RedisFeatureStoreBuilder} for information on available configuration options and what they do.
114113
*
115114
* @param builder the configured builder to construct the store with.
@@ -127,7 +126,6 @@ protected RedisFeatureStore(RedisFeatureStoreBuilder builder) {
127126

128127
/**
129128
* Creates a new store instance that connects to Redis with a default connection (localhost port 6379) and no in-memory cache.
130-
*
131129
*/
132130
public RedisFeatureStore() {
133131
pool = new JedisPool(getPoolConfig(), "localhost");
@@ -168,8 +166,9 @@ public Optional<FeatureFlag> load(String key) throws Exception {
168166
/**
169167
* Configures the instance to use a "refresh after write" cache. This will not automatically evict stale values, allowing them to be returned if failures
170168
* occur when updating them. Optionally set the cache to refresh values asynchronously, which always returns the previously cached value immediately.
169+
*
171170
* @param cacheTimeSecs the length of time in seconds, after a {@link FeatureFlag} value is created that it should be refreshed.
172-
* @param asyncRefresh makes the refresh asynchronous or not.
171+
* @param asyncRefresh makes the refresh asynchronous or not.
173172
*/
174173
private void createRefreshCache(long cacheTimeSecs, boolean asyncRefresh) {
175174
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(CACHE_REFRESH_THREAD_POOL_NAME_FORMAT).setDaemon(true).build();
@@ -184,6 +183,7 @@ private void createRefreshCache(long cacheTimeSecs, boolean asyncRefresh) {
184183

185184
/**
186185
* Configures the instance to use an "expire after write" cache. This will evict stale values and block while loading the latest from Redis.
186+
*
187187
* @param cacheTimeSecs the length of time in seconds, after a {@link FeatureFlag} value is created that it should be automatically removed.
188188
*/
189189
private void createExpiringCache(long cacheTimeSecs) {
@@ -234,15 +234,16 @@ public FeatureFlag get(String key) {
234234
@Override
235235
public Map<String, FeatureFlag> all() {
236236
try (Jedis jedis = pool.getResource()) {
237-
Map<String,String> featuresJson = jedis.hgetAll(featuresKey());
237+
Map<String, String> featuresJson = jedis.hgetAll(featuresKey());
238238
Map<String, FeatureFlag> result = new HashMap<>();
239239
Gson gson = new Gson();
240240

241-
Type type = new TypeToken<FeatureFlag>() {}.getType();
241+
Type type = new TypeToken<FeatureFlag>() {
242+
}.getType();
242243

243244
for (Map.Entry<String, String> entry : featuresJson.entrySet()) {
244-
FeatureFlag featureFlag = gson.fromJson(entry.getValue(), type);
245-
if (!featureFlag.isDeleted()){
245+
FeatureFlag featureFlag = gson.fromJson(entry.getValue(), type);
246+
if (!featureFlag.isDeleted()) {
246247
result.put(entry.getKey(), featureFlag);
247248
}
248249
}
@@ -264,7 +265,7 @@ public void init(Map<String, FeatureFlag> features) {
264265

265266
t.del(featuresKey());
266267

267-
for (FeatureFlag f: features.values()) {
268+
for (FeatureFlag f : features.values()) {
268269
t.hset(featuresKey(), f.getKey(), gson.toJson(f));
269270
}
270271

@@ -276,7 +277,7 @@ public void init(Map<String, FeatureFlag> features) {
276277
* Deletes the feature associated with the specified key, if it exists and its version
277278
* is less than or equal to the specified version.
278279
*
279-
* @param key the key of the feature to be deleted
280+
* @param key the key of the feature to be deleted
280281
* @param version the version for the delete operation
281282
*/
282283
@Override
@@ -287,7 +288,7 @@ public void delete(String key, int version) {
287288
jedis = pool.getResource();
288289
jedis.watch(featuresKey());
289290

290-
FeatureFlag feature = getRedis(key);
291+
FeatureFlag feature = getRedis(key, jedis);
291292

292293
if (feature != null && feature.getVersion() >= version) {
293294
logger.warn("Attempted to delete flag: " + key + " version: " + feature.getVersion() +
@@ -303,8 +304,7 @@ public void delete(String key, int version) {
303304
if (cache != null) {
304305
cache.invalidate(key);
305306
}
306-
}
307-
finally {
307+
} finally {
308308
if (jedis != null) {
309309
jedis.unwatch();
310310
jedis.close();
@@ -327,7 +327,7 @@ public void upsert(String key, FeatureFlag feature) {
327327
Gson gson = new Gson();
328328
jedis.watch(featuresKey());
329329

330-
FeatureFlag f = getRedis(key);
330+
FeatureFlag f = getRedis(key, jedis);
331331

332332
if (f != null && f.getVersion() >= feature.getVersion()) {
333333
logger.warn("Attempted to update flag: " + key + " version: " + f.getVersion() +
@@ -368,6 +368,7 @@ public boolean initialized() {
368368

369369
/**
370370
* Releases all resources associated with the store. The store must no longer be used once closed.
371+
*
371372
* @throws IOException
372373
*/
373374
public void close() throws IOException {
@@ -404,24 +405,29 @@ private Boolean getInit() {
404405
}
405406

406407
private FeatureFlag getRedis(String key) {
407-
try (Jedis jedis = pool.getResource()){
408-
Gson gson = new Gson();
409-
String featureJson = jedis.hget(featuresKey(), key);
408+
try (Jedis jedis = pool.getResource()) {
409+
return getRedis(key, jedis);
410+
}
411+
}
410412

411-
if (featureJson == null) {
412-
logger.debug("[get] Key: " + key + " not found in feature store. Returning null");
413-
return null;
414-
}
413+
private FeatureFlag getRedis(String key, Jedis jedis) {
414+
Gson gson = new Gson();
415+
String featureJson = jedis.hget(featuresKey(), key);
415416

416-
Type type = new TypeToken<FeatureFlag>() {}.getType();
417-
FeatureFlag f = gson.fromJson(featureJson, type);
417+
if (featureJson == null) {
418+
logger.debug("[get] Key: " + key + " not found in feature store. Returning null");
419+
return null;
420+
}
418421

419-
if (f.isDeleted()) {
420-
logger.debug("[get] Key: " + key + " has been deleted. Returning null");
421-
return null;
422-
}
423-
return f;
422+
Type type = new TypeToken<FeatureFlag>() {
423+
}.getType();
424+
FeatureFlag f = gson.fromJson(featureJson, type);
425+
426+
if (f.isDeleted()) {
427+
logger.debug("[get] Key: " + key + " has been deleted. Returning null");
428+
return null;
424429
}
430+
return f;
425431
}
426432

427433
private static JedisPoolConfig getPoolConfig() {

0 commit comments

Comments
 (0)