From adee1cd5036714868d7c5ffdfbb8a5fcd5a3138b Mon Sep 17 00:00:00 2001 From: Gabriel Martinez Date: Tue, 1 Mar 2022 11:52:31 -0500 Subject: [PATCH] Added TTL to save operation (#7) * Added TTL to save operation * Changed demo package names. --- README.md | 9 +- configuration/centralized-cache/build.gradle | 1 - .../config/CentralizedCacheConfig.java | 2 +- .../binstash/config/HybridCacheConfig.java | 4 +- .../binstash/config/LocalCacheConfig.java | 2 +- .../binstash/model/InvalidValueException.java | 7 + .../bancolombia/binstash/model/SyncRule.java | 15 ++ .../binstash/model/api/HashStash.java | 26 ++- .../binstash/model/api/MapCache.java | 77 +++++++- .../binstash/model/api/ObjectCache.java | 13 +- .../binstash/model/api/StringStash.java | 29 ++- .../binstash/DoubleTierMapCacheUseCase.java | 30 ++- .../DoubleTierObjectCacheUseCase.java | 32 ++- .../binstash/SingleTierMapCacheUseCase.java | 10 + .../SingleTierObjectCacheUseCase.java | 18 +- .../DoubleTierMapCacheUseCaseTest.java | 24 +-- .../SingleTierObjectCacheUseCaseTest.java | 21 +- .../CacheDemoCentralizedApplication.java | 2 +- .../CentralizedExampleConfiguration.java | 12 +- .../handler/PersonCachedHandler.java | 12 +- .../handler/PersonHandler.java | 4 +- .../model/Address.java | 2 +- .../model/DummyRepo.java | 2 +- .../democentralized}/model/Person.java | 2 +- .../repository/PersonRepo.java | 8 +- .../src/main/resources/application.yaml | 5 +- .../CacheDemoHybridApplication.java | 2 +- .../config/HybridExampleConfiguration.java | 12 +- .../handler/PersonCachedHandler.java | 6 +- .../handler/PersonHandler.java | 4 +- .../model/Address.java | 2 +- .../binstash/demohybrid}/model/DummyRepo.java | 2 +- .../binstash/demohybrid}/model/Person.java | 2 +- .../demohybrid}/repository/PersonRepo.java | 8 +- .../src/main/resources/application.yaml | 6 +- .../CacheDemoLocalApplication.java | 2 +- .../config/LocalExampleConfiguration.java | 12 +- .../handler/PersonCachedHandler.java | 12 +- .../handler/PersonHandler.java | 4 +- .../{example => demolocal}/model/Address.java | 2 +- .../binstash/demolocal}/model/DummyRepo.java | 2 +- .../binstash/demolocal}/model/Person.java | 2 +- .../repository/PersonRepo.java | 8 +- .../local/src/main/resources/application.yaml | 1 - gradle.properties | 2 +- .../driven-adapters/mem-stash/build.gradle | 2 +- .../binstash/adapter/memory/MemoryStash.java | 117 ++++++++--- ...StashTest.java => MemoryMapStashTest.java} | 148 ++------------ .../adapter/redis/MemoryStringStashTest.java | 185 ++++++++++++++++++ .../binstash/adapter/redis/RedisStash.java | 38 +++- main.gradle | 8 + 51 files changed, 660 insertions(+), 298 deletions(-) create mode 100644 domain/model/src/main/java/co/com/bancolombia/binstash/model/InvalidValueException.java rename examples/centralized/src/main/java/co/com/bancolombia/binstash/{example => democentralized}/CacheDemoCentralizedApplication.java (85%) rename examples/centralized/src/main/java/co/com/bancolombia/binstash/{example => democentralized}/config/CentralizedExampleConfiguration.java (79%) rename examples/{local/src/main/java/co/com/bancolombia/binstash/example => centralized/src/main/java/co/com/bancolombia/binstash/democentralized}/handler/PersonCachedHandler.java (82%) rename examples/centralized/src/main/java/co/com/bancolombia/binstash/{example => democentralized}/handler/PersonHandler.java (88%) rename examples/centralized/src/main/java/co/com/bancolombia/binstash/{example => democentralized}/model/Address.java (78%) rename examples/centralized/src/main/java/co/com/bancolombia/binstash/{example => democentralized}/model/DummyRepo.java (65%) rename examples/{hybrid/src/main/java/co/com/bancolombia/binstash/example => centralized/src/main/java/co/com/bancolombia/binstash/democentralized}/model/Person.java (78%) rename examples/{hybrid/src/main/java/co/com/bancolombia/binstash/example => centralized/src/main/java/co/com/bancolombia/binstash/democentralized}/repository/PersonRepo.java (71%) rename examples/hybrid/src/main/java/co/com/bancolombia/binstash/{example => demohybrid}/CacheDemoHybridApplication.java (86%) rename examples/hybrid/src/main/java/co/com/bancolombia/binstash/{example => demohybrid}/config/HybridExampleConfiguration.java (85%) rename examples/{centralized/src/main/java/co/com/bancolombia/binstash/example => hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid}/handler/PersonCachedHandler.java (89%) rename examples/hybrid/src/main/java/co/com/bancolombia/binstash/{example => demohybrid}/handler/PersonHandler.java (89%) rename examples/hybrid/src/main/java/co/com/bancolombia/binstash/{example => demohybrid}/model/Address.java (79%) rename examples/{local/src/main/java/co/com/bancolombia/binstash/example => hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid}/model/DummyRepo.java (67%) rename examples/{local/src/main/java/co/com/bancolombia/binstash/example => hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid}/model/Person.java (79%) rename examples/{centralized/src/main/java/co/com/bancolombia/binstash/example => hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid}/repository/PersonRepo.java (72%) rename examples/local/src/main/java/co/com/bancolombia/binstash/{example => demolocal}/CacheDemoLocalApplication.java (86%) rename examples/local/src/main/java/co/com/bancolombia/binstash/{example => demolocal}/config/LocalExampleConfiguration.java (81%) rename examples/{hybrid/src/main/java/co/com/bancolombia/binstash/example => local/src/main/java/co/com/bancolombia/binstash/demolocal}/handler/PersonCachedHandler.java (83%) rename examples/local/src/main/java/co/com/bancolombia/binstash/{example => demolocal}/handler/PersonHandler.java (89%) rename examples/local/src/main/java/co/com/bancolombia/binstash/{example => demolocal}/model/Address.java (79%) rename examples/{hybrid/src/main/java/co/com/bancolombia/binstash/example => local/src/main/java/co/com/bancolombia/binstash/demolocal}/model/DummyRepo.java (67%) rename examples/{centralized/src/main/java/co/com/bancolombia/binstash/example => local/src/main/java/co/com/bancolombia/binstash/demolocal}/model/Person.java (80%) rename examples/local/src/main/java/co/com/bancolombia/binstash/{example => demolocal}/repository/PersonRepo.java (73%) rename infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/{MemoryStashTest.java => MemoryMapStashTest.java} (57%) create mode 100644 infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/MemoryStringStashTest.java diff --git a/README.md b/README.md index 55350df..ca446e1 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=bancolombia_bin-stash&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=bancolombia_bin-stash) [![codecov](https://codecov.io/gh/bancolombia/bin-stash/branch/master/graph/badge.svg)](https://codecov.io/gh/bancolombia/bin-stash) [![GitHub license](https://img.shields.io/github/license/Naereen/StrapDown.js.svg)](https://github.com/bancolombia/bin-stash/blob/master/LICENSE) -[![Scorecards supply-chain security](https://github.com/bancolombia/bin-stash/actions/workflows/scorecards-analysis.yml/badge.svg)](https://github.com/bancolombia/bin-stash/actions/workflows/scorecards-analysis.yml) Library for caching data: @@ -20,7 +19,7 @@ For local cache only ```gradle dependencies { - implementation 'com.github.bancolombia:bin-stash-local:1.0.2' + implementation 'com.github.bancolombia:bin-stash-local:1.0.3' } ``` @@ -28,7 +27,7 @@ For a centralized (redis) cache only ```gradle dependencies { - implementation 'com.github.bancolombia:bin-stash-centralized:1.0.2' + implementation 'com.github.bancolombia:bin-stash-centralized:1.0.3' } ``` @@ -36,7 +35,7 @@ For an hybrid (local and centralized) cache ```gradle dependencies { - implementation 'com.github.bancolombia:bin-stash-hybrid:1.0.2' + implementation 'com.github.bancolombia:bin-stash-hybrid:1.0.3' } ``` @@ -45,10 +44,8 @@ dependencies { ```yaml stash: memory: - expireTime: 10 maxSize: 10_000 redis: - expireTime: 60 host: myredis.host port: 6379 database: 0 diff --git a/configuration/centralized-cache/build.gradle b/configuration/centralized-cache/build.gradle index 8165f45..b3c57e0 100644 --- a/configuration/centralized-cache/build.gradle +++ b/configuration/centralized-cache/build.gradle @@ -1,6 +1,5 @@ dependencies { compile project(':bin-stash-usecase') - compile project(':bin-stash-memory') compile project(':bin-stash-redis') implementation 'org.springframework:spring-core' implementation 'org.springframework:spring-context' diff --git a/configuration/centralized-cache/src/main/java/co/com/bancolombia/binstash/config/CentralizedCacheConfig.java b/configuration/centralized-cache/src/main/java/co/com/bancolombia/binstash/config/CentralizedCacheConfig.java index 1655d14..376fd13 100644 --- a/configuration/centralized-cache/src/main/java/co/com/bancolombia/binstash/config/CentralizedCacheConfig.java +++ b/configuration/centralized-cache/src/main/java/co/com/bancolombia/binstash/config/CentralizedCacheConfig.java @@ -24,7 +24,7 @@ public class CentralizedCacheConfig { @Value("${stash.redis.password:}") private String password; - @Value("${stash.redis.expireTime:60}") + @Value("${stash.redis.expireTime:-1}") private int redisExpireTime; @Bean(name = "centralMemStashBean") diff --git a/configuration/hybrid-cache/src/main/java/co/com/bancolombia/binstash/config/HybridCacheConfig.java b/configuration/hybrid-cache/src/main/java/co/com/bancolombia/binstash/config/HybridCacheConfig.java index adc892f..aa6a3b4 100644 --- a/configuration/hybrid-cache/src/main/java/co/com/bancolombia/binstash/config/HybridCacheConfig.java +++ b/configuration/hybrid-cache/src/main/java/co/com/bancolombia/binstash/config/HybridCacheConfig.java @@ -18,7 +18,7 @@ @Configuration public class HybridCacheConfig { - @Value("${stash.memory.expireTime:60}") + @Value("${stash.memory.expireTime:-1}") private int localExpireTime; @Value("${stash.memory.maxSize:1000}") @@ -36,7 +36,7 @@ public class HybridCacheConfig { @Value("${stash.redis.password:}") private String password; - @Value("${stash.redis.expireTime:60}") + @Value("${stash.redis.expireTime:-1}") private int redisExpireTime; @Bean(name = "hybridMemStashBean") diff --git a/configuration/local-cache/src/main/java/co/com/bancolombia/binstash/config/LocalCacheConfig.java b/configuration/local-cache/src/main/java/co/com/bancolombia/binstash/config/LocalCacheConfig.java index 3704a7c..2f4e25d 100644 --- a/configuration/local-cache/src/main/java/co/com/bancolombia/binstash/config/LocalCacheConfig.java +++ b/configuration/local-cache/src/main/java/co/com/bancolombia/binstash/config/LocalCacheConfig.java @@ -11,7 +11,7 @@ @Configuration public class LocalCacheConfig { - @Value("${stash.memory.expireTime:60}") + @Value("${stash.memory.expireTime:-1}") private int expireTime; @Value("${stash.memory.maxSize:1000}") diff --git a/domain/model/src/main/java/co/com/bancolombia/binstash/model/InvalidValueException.java b/domain/model/src/main/java/co/com/bancolombia/binstash/model/InvalidValueException.java new file mode 100644 index 0000000..4ab82e7 --- /dev/null +++ b/domain/model/src/main/java/co/com/bancolombia/binstash/model/InvalidValueException.java @@ -0,0 +1,7 @@ +package co.com.bancolombia.binstash.model; + +public class InvalidValueException extends RuntimeException{ + public InvalidValueException(String message) { + super(message); + } +} diff --git a/domain/model/src/main/java/co/com/bancolombia/binstash/model/SyncRule.java b/domain/model/src/main/java/co/com/bancolombia/binstash/model/SyncRule.java index 2af4b0d..f4219a8 100644 --- a/domain/model/src/main/java/co/com/bancolombia/binstash/model/SyncRule.java +++ b/domain/model/src/main/java/co/com/bancolombia/binstash/model/SyncRule.java @@ -1,6 +1,21 @@ package co.com.bancolombia.binstash.model; +/** + * Synchronization rule to be evaluated by the double tier cache in order to decide if a key/value + * should be synchronized upstream or downstream. + */ @FunctionalInterface public interface SyncRule { + + /** + * Logic to decide if a key (keyArg) should be synchronized
SyncType.UPSTREAM
or + *
SyncType.DOWNSTREAM
, being the local cache always thw downstream and the centralized + * the downstream. + * + * @param keyArg the key to be evaluated + * @param syncType the sync to be evaluated. It is either
SyncType.UPSTREAM
or + * *
SyncType.DOWNSTREAM
+ * @return true if the key should be propagated in the defined
SyncType
direction, false otherwise. + */ boolean apply(String keyArg, SyncType syncType); } diff --git a/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/HashStash.java b/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/HashStash.java index 528675e..f346938 100644 --- a/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/HashStash.java +++ b/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/HashStash.java @@ -6,7 +6,7 @@ import java.util.Set; /** - * Repo for storing Map<String, String> data. + * Repo for storing hash collections of data. */ public interface HashStash { @@ -18,6 +18,15 @@ public interface HashStash { */ Mono> hSave(String key, Map value); + /** + * Saves a Map value under key, indicating a Time to live for the data in the cache + * @param key key value to index map + * @param value map to store in cache + * @param ttl time to live in seconds + * @return inserted map. + */ + Mono> hSave(String key, Map value, int ttl); + /** * Adds/Updates fileds in map. * @@ -28,6 +37,17 @@ public interface HashStash { */ Mono hSave(String key, String field, String value); + /** + * Adds/Updates fileds in map, possibly indicating a ttl for the key in the cache. + * + * @param key key value to index map + * @param field field to update/add into map + * @param value value to set field to + * @param ttl time to live in seconds + * @return field value updated/added in map. + */ + Mono hSave(String key, String field, String value, int ttl); + /** * Gets field value from map * @@ -44,6 +64,10 @@ public interface HashStash { */ Mono> hGetAll(String key); + /** + * Gets a set of all keys currently stored + * @return Set o f keys + */ Mono> keySet(); /** diff --git a/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/MapCache.java b/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/MapCache.java index 4b4fbdd..559da6c 100644 --- a/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/MapCache.java +++ b/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/MapCache.java @@ -6,30 +6,101 @@ import java.util.Set; /** - * Cache for storing <String, Map> data. + * Cache API for storing multiple
Map<String, String>
data in cache. Implementor will use the
HashStash
+ * as repository. */ public interface MapCache { + /** + * Stores the argument Map into the cache under the provided key. + * @param key the key under which the map should be stored into. + * @param value the map to store + * @return the original map. + */ Mono> saveMap(String key, Map value); + /** + * Stores the argument Map into the cache under the provided key, and setting the expiration + * of the key + * @param key the key under which the map should be stored into. + * @param value the map to store + * @param ttl the time to live of the key in the cache + * @return the original map. + */ + Mono> saveMap(String key, Map value, int ttl); + + /** + * Creates a new map, if it doesn't previously exist in the cache with the provided
key
, and stores + * the field and value into such map. If the map exists, then add/updates the field-value. + * @param key the key under which the map should be stored into. + * @param field the field to store + * @param value the value to store + * @return the original map. + */ Mono saveMap(String key, String field, String value); + /** + * Creates a new map, if it doesn't previously exist in the cache with the provided
key
, and stores + * the field and value into such map. If the map exists, then add/updates the field-value. + * @param key the key under which the map should be stored into. + * @param field the field to store + * @param value the value to store + * @param ttl the time to live of the key in the cache + * @return the original map. + */ + Mono saveMap(String key, String field, String value, int ttl); + + /** + * Fetches a value stored in a map in the cache. + * @param key the key under which the map exists in the cache. + * @param field the name of the field in the map. + * @return the string value associated to the field name. + */ Mono getMap(String key, String field); + /** + * Fetches the whole map stored under a key in the cache. + * @param key the key under which the map exists in the cache. + * @return the Map object. + */ Mono> getMap(String key); + /** + * Checks whether a map is stored under a given key in the cache. + * @param key the key to check if exists in the cache. + * @return true if there is a map stored in the cache with such key, false otherwise. + */ Mono existsMap(String key); + /** + * Checks whether field exists within a map stored under a given key in the cache. + * @param key the key to check if exists as a map in the cache. + * @param field the field value to check within the map. + * @return true if there is a map stored in the cache with such key, and a field with a given name in the map, + * or false otherwise. + */ Mono existsMap(String key, String field); /** - * obtains a Set with all keys stored in cache - * @return a Mono containing a Set of strings mapping each key that exists in cache. + * Retrieves all keys existing in the cache. Note this operation will return ALL keys existing in the underlying + * cache whether those keys represent maps or single key-values. + * @return a set of all keys. */ Mono> keySet(); + /** + * Removes a map identified by key, effectively removing all field-value pairs. + * @param key the key under which the map is stored in the cache. + * @return true if the map was evicted, false otherwise. + */ Mono evictMap(String key); + /** + * Removes a field from a map identified by key, effectively removing a single field-value pair. + * @param key the key under which the map is stored in the cache. + * @param field the field under which the value is stored in the map. + * @return true if the field-value was removed from map, false otherwise. + */ Mono evictMap(String key, String field); } diff --git a/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/ObjectCache.java b/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/ObjectCache.java index 68b2796..1234df3 100644 --- a/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/ObjectCache.java +++ b/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/ObjectCache.java @@ -5,7 +5,9 @@ import java.util.Set; /** - * Cache for storing <String, T> data. + * API for storing Objects in cache. Implementor will use
Stash
as a repository, and Objects will be + * serialized into
String
before invoking the appropriate
Stash
save operations, and deserialized + * back into the corresponding Type on get operations. */ public interface ObjectCache { @@ -17,6 +19,15 @@ public interface ObjectCache { */ Mono save(String key, T value); + /** + * Save value to cache, alternatively specifying a TTL for the key + * @param key key to index value + * @param value value to store + * @param ttl time key should live in cache + * @return value stored + */ + Mono save(String key, T value, int ttl); + /** * Gets an element from cache * @param key key to which value was stored diff --git a/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/StringStash.java b/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/StringStash.java index dfc2c77..c1498f6 100644 --- a/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/StringStash.java +++ b/domain/model/src/main/java/co/com/bancolombia/binstash/model/api/StringStash.java @@ -5,10 +5,19 @@ import java.util.Set; /** - * Repo for storing key/value string data. + * Repo for storing key/value
String
data. */ public interface StringStash { + /** + * Saves a key-value in a repository + * @param key key to store related value + * @param value value to be stored + * @param ttl time the key should live in the stash + * @return the same value stored. + */ + Mono save(String key, String value, int ttl); + /** * Saves a key-value in a repository * @param key key to store related value @@ -24,12 +33,30 @@ public interface StringStash { */ Mono get(String key); + /** + * Gets a set of all keys currently stored + * @return Set o f keys + */ Mono> keySet(); + /** + * Checks if a given key exists in the repository + * @param key the key to be checked. + * @return true if the key exists, false otherwise. + */ Mono exists(String key); + /** + * Remove the specified key, and its value, from the repo, if such key exists. + * @param key the key to be evicted. + * @return true if the key and corresponding value were evicted. + */ Mono evict(String key); + /** + * Prune whole repository, evicting all keys and its associated values. + * @return true if the process completed successfully, false otherwise. + */ Mono evictAll(); } diff --git a/domain/usecase/src/main/java/co/com/bancolombia/binstash/DoubleTierMapCacheUseCase.java b/domain/usecase/src/main/java/co/com/bancolombia/binstash/DoubleTierMapCacheUseCase.java index 1146f93..b5f1c51 100644 --- a/domain/usecase/src/main/java/co/com/bancolombia/binstash/DoubleTierMapCacheUseCase.java +++ b/domain/usecase/src/main/java/co/com/bancolombia/binstash/DoubleTierMapCacheUseCase.java @@ -25,28 +25,38 @@ public DoubleTierMapCacheUseCase(MapCache localCache, @Override public Mono> saveMap(String key, Map value) { - return localCache.saveMap(key, value) + return saveMap(key, value, -1); + } + + @Override + public Mono> saveMap(String key, Map value, int ttl) { + return localCache.saveMap(key, value, ttl) .doAfterTerminate(() -> - Mono.just(ruleEvaluatorUseCase.evalForUpstreamSync(key)) - .subscribeOn(elastic_scheduler) - .filter(shouldSync -> shouldSync) - .flatMap(shouldSync -> centralizedCache.existsMap(key)) - .filter(elementExistsInDistCache -> !elementExistsInDistCache) - .flatMap(exists -> centralizedCache.saveMap(key, value)) - .subscribe() + Mono.just(ruleEvaluatorUseCase.evalForUpstreamSync(key)) + .subscribeOn(elastic_scheduler) + .filter(shouldSync -> shouldSync) + .flatMap(shouldSync -> centralizedCache.existsMap(key)) + .filter(elementExistsInDistCache -> !elementExistsInDistCache) + .flatMap(exists -> centralizedCache.saveMap(key, value, ttl)) + .subscribe() ); } @Override public Mono saveMap(String key, String field, String value) { - return localCache.saveMap(key, field, value) + return saveMap(key, field, value, -1); + } + + @Override + public Mono saveMap(String key, String field, String value, int ttl) { + return localCache.saveMap(key, field, value, ttl) .doAfterTerminate(() -> Mono.just(ruleEvaluatorUseCase.evalForUpstreamSync(key)) .subscribeOn(elastic_scheduler) .filter(shouldSync -> shouldSync) .flatMap(shouldSync -> centralizedCache.existsMap(key, field)) .filter(elementExistsInDistCache -> !elementExistsInDistCache) - .flatMap(exists -> centralizedCache.saveMap(key, field, value)) + .flatMap(exists -> centralizedCache.saveMap(key, field, value, ttl)) .subscribe() ); } diff --git a/domain/usecase/src/main/java/co/com/bancolombia/binstash/DoubleTierObjectCacheUseCase.java b/domain/usecase/src/main/java/co/com/bancolombia/binstash/DoubleTierObjectCacheUseCase.java index 4ae7d27..cc9cc43 100644 --- a/domain/usecase/src/main/java/co/com/bancolombia/binstash/DoubleTierObjectCacheUseCase.java +++ b/domain/usecase/src/main/java/co/com/bancolombia/binstash/DoubleTierObjectCacheUseCase.java @@ -22,18 +22,32 @@ public DoubleTierObjectCacheUseCase(ObjectCache localCache, this.ruleEvaluatorUseCase = ruleEvaluatorUseCase; } + @Override + public Mono save(String key, T value, int ttl) { + return localCache.save(key, value, ttl) + .doAfterTerminate(() -> + Mono.just(ruleEvaluatorUseCase.evalForUpstreamSync(key)) + .subscribeOn(elastic_scheduler) + .filter(shouldSync -> shouldSync) + .flatMap(shouldSync -> centralizedCache.exists(key)) + .filter(elementExistsInDistCache -> !elementExistsInDistCache) + .flatMap(exists -> centralizedCache.save(key, value, ttl)) + .subscribe() + ); + } + @Override public Mono save(String key, T value) { return localCache.save(key, value) - .doAfterTerminate(() -> - Mono.just(ruleEvaluatorUseCase.evalForUpstreamSync(key)) - .subscribeOn(elastic_scheduler) - .filter(shouldSync -> shouldSync) - .flatMap(shouldSync -> centralizedCache.exists(key)) - .filter(elementExistsInDistCache -> !elementExistsInDistCache) - .flatMap(exists -> centralizedCache.save(key, value)) - .subscribe() - ); + .doAfterTerminate(() -> + Mono.just(ruleEvaluatorUseCase.evalForUpstreamSync(key)) + .subscribeOn(elastic_scheduler) + .filter(shouldSync -> shouldSync) + .flatMap(shouldSync -> centralizedCache.exists(key)) + .filter(elementExistsInDistCache -> !elementExistsInDistCache) + .flatMap(exists -> centralizedCache.save(key, value)) + .subscribe() + ); } @Override diff --git a/domain/usecase/src/main/java/co/com/bancolombia/binstash/SingleTierMapCacheUseCase.java b/domain/usecase/src/main/java/co/com/bancolombia/binstash/SingleTierMapCacheUseCase.java index 9087cd5..04e5730 100644 --- a/domain/usecase/src/main/java/co/com/bancolombia/binstash/SingleTierMapCacheUseCase.java +++ b/domain/usecase/src/main/java/co/com/bancolombia/binstash/SingleTierMapCacheUseCase.java @@ -20,11 +20,21 @@ public Mono> saveMap(String key, Map value) return stash.hSave(key, value); } + @Override + public Mono> saveMap(String key, Map value, int ttl) { + return stash.hSave(key, value, ttl); + } + @Override public Mono saveMap(String key, String field, String value) { return stash.hSave(key, field, value); } + @Override + public Mono saveMap(String key, String field, String value, int ttl) { + return stash.hSave(key, field, value, ttl); + } + @Override public Mono getMap(String key, String field) { return stash.hGet(key, field); diff --git a/domain/usecase/src/main/java/co/com/bancolombia/binstash/SingleTierObjectCacheUseCase.java b/domain/usecase/src/main/java/co/com/bancolombia/binstash/SingleTierObjectCacheUseCase.java index 288c70b..c814001 100644 --- a/domain/usecase/src/main/java/co/com/bancolombia/binstash/SingleTierObjectCacheUseCase.java +++ b/domain/usecase/src/main/java/co/com/bancolombia/binstash/SingleTierObjectCacheUseCase.java @@ -1,5 +1,6 @@ package co.com.bancolombia.binstash; +import co.com.bancolombia.binstash.model.InvalidValueException; import co.com.bancolombia.binstash.model.api.ObjectCache; import co.com.bancolombia.binstash.model.api.StringStash; import com.fasterxml.jackson.core.type.TypeReference; @@ -18,10 +19,19 @@ public class SingleTierObjectCacheUseCase implements ObjectCache { @Override public Mono save(String key, T value) { - return Mono.just(value) - .map(this::serialize) - .flatMap(serialized -> cache.save(key, serialized)) - .map(r -> value); + return save(key, value, -1); + } + + @Override + public Mono save(String key, T value, int ttl) { + if (value == null) { + return Mono.error(new InvalidValueException("Value cannot be null")); + } else { + return Mono.just(value) + .map(this::serialize) + .flatMap(serialized -> cache.save(key, serialized, ttl)) + .map(r -> value); + } } @Override diff --git a/domain/usecase/src/test/java/co/com/bancolombia/binstash/DoubleTierMapCacheUseCaseTest.java b/domain/usecase/src/test/java/co/com/bancolombia/binstash/DoubleTierMapCacheUseCaseTest.java index f0be1e5..93472b2 100644 --- a/domain/usecase/src/test/java/co/com/bancolombia/binstash/DoubleTierMapCacheUseCaseTest.java +++ b/domain/usecase/src/test/java/co/com/bancolombia/binstash/DoubleTierMapCacheUseCaseTest.java @@ -51,10 +51,10 @@ void testCreate() { @DisplayName("save local map cache, sync upstream") void testSave() { - when(localCache.saveMap(anyString(), any(Map.class))).thenReturn(Mono.just(demoMap)); + when(localCache.saveMap(anyString(), any(Map.class), anyInt())).thenReturn(Mono.just(demoMap)); when(ruleEvaluatorUseCase.evalForUpstreamSync(anyString())).thenReturn(true); when(centralizedCache.existsMap(anyString())).thenReturn(Mono.just(false)); - when(centralizedCache.saveMap(anyString(), any(Map.class))).thenReturn(Mono.just(demoMap)); + when(centralizedCache.saveMap(anyString(), any(Map.class), anyInt())).thenReturn(Mono.just(demoMap)); StepVerifier.create(cache.saveMap("pparker", demoMap)) .expectSubscription() @@ -62,8 +62,8 @@ void testSave() { .expectComplete() .verify(); - verify(localCache).saveMap("pparker", demoMap); - verify(centralizedCache, timeout(1000)).saveMap("pparker", demoMap); + verify(localCache).saveMap("pparker", demoMap, -1); + verify(centralizedCache, timeout(1000)).saveMap("pparker", demoMap, -1); } @SneakyThrows @@ -71,7 +71,7 @@ void testSave() { @DisplayName("save local map cache, try sync upstream, key exists") void testSave2() { - when(localCache.saveMap(anyString(), any(Map.class))).thenReturn(Mono.just(demoMap)); + when(localCache.saveMap(anyString(), any(Map.class), anyInt())).thenReturn(Mono.just(demoMap)); // when(ruleEvaluatorUseCase.evalForUpstreamSync(anyString())).thenReturn(true); // when(centralizedCache.existsMap(anyString())).thenReturn(Mono.just(true)); @@ -81,7 +81,7 @@ void testSave2() { .expectComplete() .verify(); - verify(localCache).saveMap("pparker", demoMap); + verify(localCache).saveMap("pparker", demoMap, -1); // verify(centralizedCache, timeout(2000).times(0)).saveMap("pparker", demoMap); } @@ -90,7 +90,7 @@ void testSave2() { @DisplayName("save local map cache, dont sync upstream") void testSave3() { - when(localCache.saveMap(anyString(), any(Map.class))).thenReturn(Mono.just(demoMap)); + when(localCache.saveMap(anyString(), any(Map.class), anyInt())).thenReturn(Mono.just(demoMap)); when(ruleEvaluatorUseCase.evalForUpstreamSync(anyString())).thenReturn(false); StepVerifier.create(cache.saveMap("pparker", demoMap)) @@ -101,7 +101,7 @@ void testSave3() { .hasNotDiscardedElements() .hasNotDroppedElements(); - verify(localCache).saveMap("pparker", demoMap); + verify(localCache).saveMap("pparker", demoMap, -1); verify(centralizedCache, timeout(1000).times(0)).saveMap("pparker", demoMap); } @@ -110,10 +110,10 @@ void testSave3() { @Test @DisplayName("save map prop in local cache, sync upstream") void testSaveProp() { - when(localCache.saveMap(anyString(), anyString(), anyString())).thenReturn(Mono.just("NY")); + when(localCache.saveMap(anyString(), anyString(), anyString(), anyInt())).thenReturn(Mono.just("NY")); when(ruleEvaluatorUseCase.evalForUpstreamSync(anyString())).thenReturn(true); when(centralizedCache.existsMap(anyString(), anyString())).thenReturn(Mono.just(false)); - when(centralizedCache.saveMap(anyString(), anyString(), anyString())).thenReturn(Mono.just("NY")); + when(centralizedCache.saveMap(anyString(), anyString(), anyString(), anyInt())).thenReturn(Mono.just("NY")); StepVerifier.create(cache.saveMap("pparker", "city", "NY")) .expectSubscription() @@ -123,8 +123,8 @@ void testSaveProp() { .hasNotDiscardedElements() .hasNotDroppedElements(); - verify(localCache).saveMap("pparker", "city", "NY"); - verify(centralizedCache, timeout(1000)).saveMap("pparker", "city", "NY"); + verify(localCache).saveMap("pparker", "city", "NY", -1); + verify(centralizedCache, timeout(1000)).saveMap("pparker", "city", "NY", -1); } diff --git a/domain/usecase/src/test/java/co/com/bancolombia/binstash/SingleTierObjectCacheUseCaseTest.java b/domain/usecase/src/test/java/co/com/bancolombia/binstash/SingleTierObjectCacheUseCaseTest.java index 34785a9..9f46d14 100644 --- a/domain/usecase/src/test/java/co/com/bancolombia/binstash/SingleTierObjectCacheUseCaseTest.java +++ b/domain/usecase/src/test/java/co/com/bancolombia/binstash/SingleTierObjectCacheUseCaseTest.java @@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -77,7 +78,7 @@ void testCreate() { void testSave() { assert cache != null; - when(mockedStash.save(anyString(), anyString())).thenReturn(Mono.just(serializedPerson)); + when(mockedStash.save(anyString(), anyString(), eq(-1))).thenReturn(Mono.just(serializedPerson)); when(mockedStash.evictAll()).thenReturn(Mono.just(true)); StepVerifier.create(cache.save("pparker", p)) @@ -86,7 +87,7 @@ void testSave() { .expectComplete() .verify(); - verify(mockedStash).save("pparker", serializedPerson); + verify(mockedStash).save("pparker", serializedPerson, -1); } @Test @@ -105,7 +106,7 @@ void testSaveList() { e.printStackTrace(); } - when(mockedStash.save(anyString(), anyString())).thenReturn(Mono.just(serializedListOfPerson)); + when(mockedStash.save(anyString(), anyString(), eq(-1))).thenReturn(Mono.just(serializedListOfPerson)); StepVerifier.create(cache2.save("pparker", List.of(p))) .expectSubscription() @@ -113,7 +114,7 @@ void testSaveList() { .expectComplete() .verify(); - verify(mockedStash).save("pparker", serializedListOfPerson); + verify(mockedStash).save("pparker", serializedListOfPerson, -1); } @@ -122,7 +123,7 @@ void testSaveList() { void testGet() { assert cache != null; - when(mockedStash.save(anyString(), anyString())).thenReturn(Mono.just(serializedPerson)); + when(mockedStash.save(anyString(), anyString(), eq(-1))).thenReturn(Mono.just(serializedPerson)); when(mockedStash.get(anyString())).thenReturn(Mono.just(serializedPerson)); Mono personMono = cache.save("pparker", p) @@ -137,7 +138,7 @@ void testGet() { .expectComplete() .verify(); - verify(mockedStash).save("pparker", serializedPerson); + verify(mockedStash).save("pparker", serializedPerson, -1); verify(mockedStash).get("pparker"); } @@ -156,7 +157,7 @@ void testGetList() { e.printStackTrace(); } - when(mockedStash.save(anyString(), anyString())).thenReturn(Mono.just(serializedListOfPerson)); + when(mockedStash.save(anyString(), anyString(), eq(-1))).thenReturn(Mono.just(serializedListOfPerson)); when(mockedStash.get(anyString())).thenReturn(Mono.just(serializedListOfPerson)); Mono> persons = cache2.save("persons", List.of(p)) @@ -168,7 +169,7 @@ void testGetList() { .expectComplete() .verify(); - verify(mockedStash).save("persons", serializedListOfPerson); + verify(mockedStash).save("persons", serializedListOfPerson, -1); verify(mockedStash).get("persons"); } @@ -187,7 +188,7 @@ void testGetMap() { e.printStackTrace(); } - when(mockedStash.save(anyString(), anyString())).thenReturn(Mono.just(serializedMapOfPerson)); + when(mockedStash.save(anyString(), anyString(), eq(-1))).thenReturn(Mono.just(serializedMapOfPerson)); when(mockedStash.get(anyString())).thenReturn(Mono.just(serializedMapOfPerson)); Mono> persons = cache2.save("persons", Map.of("p1", p)) @@ -200,7 +201,7 @@ void testGetMap() { .expectComplete() .verify(); - verify(mockedStash).save("persons", serializedMapOfPerson); + verify(mockedStash).save("persons", serializedMapOfPerson, -1); verify(mockedStash).get("persons"); } diff --git a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/CacheDemoCentralizedApplication.java b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/CacheDemoCentralizedApplication.java similarity index 85% rename from examples/centralized/src/main/java/co/com/bancolombia/binstash/example/CacheDemoCentralizedApplication.java rename to examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/CacheDemoCentralizedApplication.java index 683119d..3202eb2 100644 --- a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/CacheDemoCentralizedApplication.java +++ b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/CacheDemoCentralizedApplication.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example; +package co.com.bancolombia.binstash.democentralized; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/config/CentralizedExampleConfiguration.java b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/config/CentralizedExampleConfiguration.java similarity index 79% rename from examples/centralized/src/main/java/co/com/bancolombia/binstash/example/config/CentralizedExampleConfiguration.java rename to examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/config/CentralizedExampleConfiguration.java index fdeb515..7784a67 100644 --- a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/config/CentralizedExampleConfiguration.java +++ b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/config/CentralizedExampleConfiguration.java @@ -1,11 +1,11 @@ -package co.com.bancolombia.binstash.example.config; +package co.com.bancolombia.binstash.democentralized.config; import co.com.bancolombia.binstash.CentralizedCacheFactory; -import co.com.bancolombia.binstash.example.handler.PersonCachedHandler; -import co.com.bancolombia.binstash.example.handler.PersonHandler; -import co.com.bancolombia.binstash.example.model.DummyRepo; -import co.com.bancolombia.binstash.example.model.Person; -import co.com.bancolombia.binstash.example.repository.PersonRepo; +import co.com.bancolombia.binstash.democentralized.handler.PersonCachedHandler; +import co.com.bancolombia.binstash.democentralized.handler.PersonHandler; +import co.com.bancolombia.binstash.democentralized.model.DummyRepo; +import co.com.bancolombia.binstash.democentralized.model.Person; +import co.com.bancolombia.binstash.democentralized.repository.PersonRepo; import co.com.bancolombia.binstash.model.api.ObjectCache; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; diff --git a/examples/local/src/main/java/co/com/bancolombia/binstash/example/handler/PersonCachedHandler.java b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/handler/PersonCachedHandler.java similarity index 82% rename from examples/local/src/main/java/co/com/bancolombia/binstash/example/handler/PersonCachedHandler.java rename to examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/handler/PersonCachedHandler.java index 702b394..b6234bc 100644 --- a/examples/local/src/main/java/co/com/bancolombia/binstash/example/handler/PersonCachedHandler.java +++ b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/handler/PersonCachedHandler.java @@ -1,7 +1,7 @@ -package co.com.bancolombia.binstash.example.handler; +package co.com.bancolombia.binstash.democentralized.handler; -import co.com.bancolombia.binstash.example.model.DummyRepo; -import co.com.bancolombia.binstash.example.model.Person; +import co.com.bancolombia.binstash.democentralized.model.DummyRepo; +import co.com.bancolombia.binstash.democentralized.model.Person; import co.com.bancolombia.binstash.model.api.ObjectCache; import lombok.extern.java.Log; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +20,8 @@ @Component public class PersonCachedHandler { + private static final int EXPIRE_AFTER = 10; + public final DummyRepo dummyRepo; public final ObjectCache objectCache; @@ -39,11 +41,13 @@ public Mono fetchWithCache(ServerRequest request) { .lookup(k -> objectCache.get(k, Person.class) .map(Signal::next), key) .onCacheMissResume(dummyRepo.findByName(key)) - .andWriteWith((k, sig) -> objectCache.save(k, sig.get()).then()); + // write to cache, and expire element after 10 seconds + .andWriteWith((k, sig) -> objectCache.save(k, sig.get(), EXPIRE_AFTER).then()); return cached .flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON) .body(BodyInserters.fromValue(person)) ); } + } diff --git a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/handler/PersonHandler.java b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/handler/PersonHandler.java similarity index 88% rename from examples/centralized/src/main/java/co/com/bancolombia/binstash/example/handler/PersonHandler.java rename to examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/handler/PersonHandler.java index d5dba31..035126d 100644 --- a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/handler/PersonHandler.java +++ b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/handler/PersonHandler.java @@ -1,6 +1,6 @@ -package co.com.bancolombia.binstash.example.handler; +package co.com.bancolombia.binstash.democentralized.handler; -import co.com.bancolombia.binstash.example.model.DummyRepo; +import co.com.bancolombia.binstash.democentralized.model.DummyRepo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; diff --git a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/model/Address.java b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/model/Address.java similarity index 78% rename from examples/centralized/src/main/java/co/com/bancolombia/binstash/example/model/Address.java rename to examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/model/Address.java index bfcb3a8..f3e64a5 100644 --- a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/model/Address.java +++ b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/model/Address.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example.model; +package co.com.bancolombia.binstash.democentralized.model; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/model/DummyRepo.java b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/model/DummyRepo.java similarity index 65% rename from examples/centralized/src/main/java/co/com/bancolombia/binstash/example/model/DummyRepo.java rename to examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/model/DummyRepo.java index 2371c88..9753c54 100644 --- a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/model/DummyRepo.java +++ b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/model/DummyRepo.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example.model; +package co.com.bancolombia.binstash.democentralized.model; import reactor.core.publisher.Mono; diff --git a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/model/Person.java b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/model/Person.java similarity index 78% rename from examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/model/Person.java rename to examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/model/Person.java index f274ccb..fe65c6c 100644 --- a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/model/Person.java +++ b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/model/Person.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example.model; +package co.com.bancolombia.binstash.democentralized.model; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/repository/PersonRepo.java b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/repository/PersonRepo.java similarity index 71% rename from examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/repository/PersonRepo.java rename to examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/repository/PersonRepo.java index 1d99bc9..f0e79e4 100644 --- a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/repository/PersonRepo.java +++ b/examples/centralized/src/main/java/co/com/bancolombia/binstash/democentralized/repository/PersonRepo.java @@ -1,8 +1,8 @@ -package co.com.bancolombia.binstash.example.repository; +package co.com.bancolombia.binstash.democentralized.repository; -import co.com.bancolombia.binstash.example.model.Address; -import co.com.bancolombia.binstash.example.model.DummyRepo; -import co.com.bancolombia.binstash.example.model.Person; +import co.com.bancolombia.binstash.democentralized.model.Address; +import co.com.bancolombia.binstash.democentralized.model.DummyRepo; +import co.com.bancolombia.binstash.democentralized.model.Person; import lombok.extern.java.Log; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; diff --git a/examples/centralized/src/main/resources/application.yaml b/examples/centralized/src/main/resources/application.yaml index efbc420..b6bc587 100644 --- a/examples/centralized/src/main/resources/application.yaml +++ b/examples/centralized/src/main/resources/application.yaml @@ -1,8 +1,5 @@ stash: redis: - expireTime: 60 host: localhost - port: 6379 -# database: -# password: \ No newline at end of file + port: 6379 \ No newline at end of file diff --git a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/CacheDemoHybridApplication.java b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/CacheDemoHybridApplication.java similarity index 86% rename from examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/CacheDemoHybridApplication.java rename to examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/CacheDemoHybridApplication.java index f406fec..904beb4 100644 --- a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/CacheDemoHybridApplication.java +++ b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/CacheDemoHybridApplication.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example; +package co.com.bancolombia.binstash.demohybrid; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/config/HybridExampleConfiguration.java b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/config/HybridExampleConfiguration.java similarity index 85% rename from examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/config/HybridExampleConfiguration.java rename to examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/config/HybridExampleConfiguration.java index 9f37ba6..e875438 100644 --- a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/config/HybridExampleConfiguration.java +++ b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/config/HybridExampleConfiguration.java @@ -1,11 +1,11 @@ -package co.com.bancolombia.binstash.example.config; +package co.com.bancolombia.binstash.demohybrid.config; -import co.com.bancolombia.binstash.example.handler.PersonCachedHandler; -import co.com.bancolombia.binstash.example.handler.PersonHandler; -import co.com.bancolombia.binstash.example.model.Person; -import co.com.bancolombia.binstash.example.repository.PersonRepo; +import co.com.bancolombia.binstash.demohybrid.handler.PersonCachedHandler; +import co.com.bancolombia.binstash.demohybrid.handler.PersonHandler; +import co.com.bancolombia.binstash.demohybrid.model.Person; +import co.com.bancolombia.binstash.demohybrid.repository.PersonRepo; import co.com.bancolombia.binstash.HybridCacheFactory; -import co.com.bancolombia.binstash.example.model.DummyRepo; +import co.com.bancolombia.binstash.demohybrid.model.DummyRepo; import co.com.bancolombia.binstash.model.SyncRule; import co.com.bancolombia.binstash.model.api.ObjectCache; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/handler/PersonCachedHandler.java b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/handler/PersonCachedHandler.java similarity index 89% rename from examples/centralized/src/main/java/co/com/bancolombia/binstash/example/handler/PersonCachedHandler.java rename to examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/handler/PersonCachedHandler.java index 358f7dd..f203102 100644 --- a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/handler/PersonCachedHandler.java +++ b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/handler/PersonCachedHandler.java @@ -1,7 +1,7 @@ -package co.com.bancolombia.binstash.example.handler; +package co.com.bancolombia.binstash.demohybrid.handler; -import co.com.bancolombia.binstash.example.model.DummyRepo; -import co.com.bancolombia.binstash.example.model.Person; +import co.com.bancolombia.binstash.demohybrid.model.DummyRepo; +import co.com.bancolombia.binstash.demohybrid.model.Person; import co.com.bancolombia.binstash.model.api.ObjectCache; import lombok.extern.java.Log; import org.springframework.beans.factory.annotation.Autowired; diff --git a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/handler/PersonHandler.java b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/handler/PersonHandler.java similarity index 89% rename from examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/handler/PersonHandler.java rename to examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/handler/PersonHandler.java index 408df26..c81ca31 100644 --- a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/handler/PersonHandler.java +++ b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/handler/PersonHandler.java @@ -1,6 +1,6 @@ -package co.com.bancolombia.binstash.example.handler; +package co.com.bancolombia.binstash.demohybrid.handler; -import co.com.bancolombia.binstash.example.model.DummyRepo; +import co.com.bancolombia.binstash.demohybrid.model.DummyRepo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; diff --git a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/model/Address.java b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/model/Address.java similarity index 79% rename from examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/model/Address.java rename to examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/model/Address.java index bfcb3a8..c89d7c9 100644 --- a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/model/Address.java +++ b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/model/Address.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example.model; +package co.com.bancolombia.binstash.demohybrid.model; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/examples/local/src/main/java/co/com/bancolombia/binstash/example/model/DummyRepo.java b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/model/DummyRepo.java similarity index 67% rename from examples/local/src/main/java/co/com/bancolombia/binstash/example/model/DummyRepo.java rename to examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/model/DummyRepo.java index 2371c88..66fca48 100644 --- a/examples/local/src/main/java/co/com/bancolombia/binstash/example/model/DummyRepo.java +++ b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/model/DummyRepo.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example.model; +package co.com.bancolombia.binstash.demohybrid.model; import reactor.core.publisher.Mono; diff --git a/examples/local/src/main/java/co/com/bancolombia/binstash/example/model/Person.java b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/model/Person.java similarity index 79% rename from examples/local/src/main/java/co/com/bancolombia/binstash/example/model/Person.java rename to examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/model/Person.java index f274ccb..5fab2eb 100644 --- a/examples/local/src/main/java/co/com/bancolombia/binstash/example/model/Person.java +++ b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/model/Person.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example.model; +package co.com.bancolombia.binstash.demohybrid.model; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/repository/PersonRepo.java b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/repository/PersonRepo.java similarity index 72% rename from examples/centralized/src/main/java/co/com/bancolombia/binstash/example/repository/PersonRepo.java rename to examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/repository/PersonRepo.java index 1d99bc9..a8c29bb 100644 --- a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/repository/PersonRepo.java +++ b/examples/hybrid/src/main/java/co/com/bancolombia/binstash/demohybrid/repository/PersonRepo.java @@ -1,8 +1,8 @@ -package co.com.bancolombia.binstash.example.repository; +package co.com.bancolombia.binstash.demohybrid.repository; -import co.com.bancolombia.binstash.example.model.Address; -import co.com.bancolombia.binstash.example.model.DummyRepo; -import co.com.bancolombia.binstash.example.model.Person; +import co.com.bancolombia.binstash.demohybrid.model.DummyRepo; +import co.com.bancolombia.binstash.demohybrid.model.Address; +import co.com.bancolombia.binstash.demohybrid.model.Person; import lombok.extern.java.Log; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; diff --git a/examples/hybrid/src/main/resources/application.yaml b/examples/hybrid/src/main/resources/application.yaml index a0a69a4..8ed3f85 100644 --- a/examples/hybrid/src/main/resources/application.yaml +++ b/examples/hybrid/src/main/resources/application.yaml @@ -1,11 +1,7 @@ stash: memory: - expireTime: 10 maxSize: 10_000 redis: - expireTime: 60 host: localhost - port: 6379 -# database: -# password: \ No newline at end of file + port: 6379 \ No newline at end of file diff --git a/examples/local/src/main/java/co/com/bancolombia/binstash/example/CacheDemoLocalApplication.java b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/CacheDemoLocalApplication.java similarity index 86% rename from examples/local/src/main/java/co/com/bancolombia/binstash/example/CacheDemoLocalApplication.java rename to examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/CacheDemoLocalApplication.java index bfbd017..5ccb61b 100644 --- a/examples/local/src/main/java/co/com/bancolombia/binstash/example/CacheDemoLocalApplication.java +++ b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/CacheDemoLocalApplication.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example; +package co.com.bancolombia.binstash.demolocal; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/examples/local/src/main/java/co/com/bancolombia/binstash/example/config/LocalExampleConfiguration.java b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/config/LocalExampleConfiguration.java similarity index 81% rename from examples/local/src/main/java/co/com/bancolombia/binstash/example/config/LocalExampleConfiguration.java rename to examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/config/LocalExampleConfiguration.java index eace7f1..bdafd33 100644 --- a/examples/local/src/main/java/co/com/bancolombia/binstash/example/config/LocalExampleConfiguration.java +++ b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/config/LocalExampleConfiguration.java @@ -1,11 +1,11 @@ -package co.com.bancolombia.binstash.example.config; +package co.com.bancolombia.binstash.demolocal.config; -import co.com.bancolombia.binstash.example.handler.PersonCachedHandler; -import co.com.bancolombia.binstash.example.model.DummyRepo; +import co.com.bancolombia.binstash.demolocal.handler.PersonCachedHandler; +import co.com.bancolombia.binstash.demolocal.model.DummyRepo; import co.com.bancolombia.binstash.LocalCacheFactory; -import co.com.bancolombia.binstash.example.handler.PersonHandler; -import co.com.bancolombia.binstash.example.model.Person; -import co.com.bancolombia.binstash.example.repository.PersonRepo; +import co.com.bancolombia.binstash.demolocal.handler.PersonHandler; +import co.com.bancolombia.binstash.demolocal.model.Person; +import co.com.bancolombia.binstash.demolocal.repository.PersonRepo; import co.com.bancolombia.binstash.model.api.ObjectCache; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; diff --git a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/handler/PersonCachedHandler.java b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/handler/PersonCachedHandler.java similarity index 83% rename from examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/handler/PersonCachedHandler.java rename to examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/handler/PersonCachedHandler.java index 358f7dd..1161951 100644 --- a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/handler/PersonCachedHandler.java +++ b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/handler/PersonCachedHandler.java @@ -1,7 +1,7 @@ -package co.com.bancolombia.binstash.example.handler; +package co.com.bancolombia.binstash.demolocal.handler; -import co.com.bancolombia.binstash.example.model.DummyRepo; -import co.com.bancolombia.binstash.example.model.Person; +import co.com.bancolombia.binstash.demolocal.model.DummyRepo; +import co.com.bancolombia.binstash.demolocal.model.Person; import co.com.bancolombia.binstash.model.api.ObjectCache; import lombok.extern.java.Log; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +20,8 @@ @Component public class PersonCachedHandler { + private static final int EXPIRE_AFTER = 10; + public final DummyRepo dummyRepo; public final ObjectCache objectCache; @@ -39,12 +41,12 @@ public Mono fetchWithCache(ServerRequest request) { .lookup(k -> objectCache.get(k, Person.class) .map(Signal::next), key) .onCacheMissResume(dummyRepo.findByName(key)) - .andWriteWith((k, sig) -> objectCache.save(k, sig.get()).then()); + // save in cache and expire after 10 seconds + .andWriteWith((k, sig) -> objectCache.save(k, sig.get(), EXPIRE_AFTER).then()); return cached .flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON) .body(BodyInserters.fromValue(person)) ); } - } diff --git a/examples/local/src/main/java/co/com/bancolombia/binstash/example/handler/PersonHandler.java b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/handler/PersonHandler.java similarity index 89% rename from examples/local/src/main/java/co/com/bancolombia/binstash/example/handler/PersonHandler.java rename to examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/handler/PersonHandler.java index 321228f..4487671 100644 --- a/examples/local/src/main/java/co/com/bancolombia/binstash/example/handler/PersonHandler.java +++ b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/handler/PersonHandler.java @@ -1,6 +1,6 @@ -package co.com.bancolombia.binstash.example.handler; +package co.com.bancolombia.binstash.demolocal.handler; -import co.com.bancolombia.binstash.example.model.DummyRepo; +import co.com.bancolombia.binstash.demolocal.model.DummyRepo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; diff --git a/examples/local/src/main/java/co/com/bancolombia/binstash/example/model/Address.java b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/model/Address.java similarity index 79% rename from examples/local/src/main/java/co/com/bancolombia/binstash/example/model/Address.java rename to examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/model/Address.java index bfcb3a8..0826533 100644 --- a/examples/local/src/main/java/co/com/bancolombia/binstash/example/model/Address.java +++ b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/model/Address.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example.model; +package co.com.bancolombia.binstash.demolocal.model; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/model/DummyRepo.java b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/model/DummyRepo.java similarity index 67% rename from examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/model/DummyRepo.java rename to examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/model/DummyRepo.java index 2371c88..b9d5d43 100644 --- a/examples/hybrid/src/main/java/co/com/bancolombia/binstash/example/model/DummyRepo.java +++ b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/model/DummyRepo.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example.model; +package co.com.bancolombia.binstash.demolocal.model; import reactor.core.publisher.Mono; diff --git a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/model/Person.java b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/model/Person.java similarity index 80% rename from examples/centralized/src/main/java/co/com/bancolombia/binstash/example/model/Person.java rename to examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/model/Person.java index f274ccb..4a6f3ce 100644 --- a/examples/centralized/src/main/java/co/com/bancolombia/binstash/example/model/Person.java +++ b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/model/Person.java @@ -1,4 +1,4 @@ -package co.com.bancolombia.binstash.example.model; +package co.com.bancolombia.binstash.demolocal.model; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/examples/local/src/main/java/co/com/bancolombia/binstash/example/repository/PersonRepo.java b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/repository/PersonRepo.java similarity index 73% rename from examples/local/src/main/java/co/com/bancolombia/binstash/example/repository/PersonRepo.java rename to examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/repository/PersonRepo.java index 632b6e7..1a0e4fd 100644 --- a/examples/local/src/main/java/co/com/bancolombia/binstash/example/repository/PersonRepo.java +++ b/examples/local/src/main/java/co/com/bancolombia/binstash/demolocal/repository/PersonRepo.java @@ -1,8 +1,8 @@ -package co.com.bancolombia.binstash.example.repository; +package co.com.bancolombia.binstash.demolocal.repository; -import co.com.bancolombia.binstash.example.model.DummyRepo; -import co.com.bancolombia.binstash.example.model.Address; -import co.com.bancolombia.binstash.example.model.Person; +import co.com.bancolombia.binstash.demolocal.model.DummyRepo; +import co.com.bancolombia.binstash.demolocal.model.Address; +import co.com.bancolombia.binstash.demolocal.model.Person; import lombok.extern.java.Log; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; diff --git a/examples/local/src/main/resources/application.yaml b/examples/local/src/main/resources/application.yaml index 5e20104..f4178cc 100644 --- a/examples/local/src/main/resources/application.yaml +++ b/examples/local/src/main/resources/application.yaml @@ -1,5 +1,4 @@ stash: memory: - expireTime: 10 maxSize: 10_000 \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index f683139..66a04ec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -version=1.0.2 +version=1.0.3 toPublish=bin-stash-model,bin-stash-usecase,bin-stash-memory,bin-stash-redis,bin-stash-local,bin-stash-centralized,bin-stash-hybrid org.gradle.daemon=true diff --git a/infrastructure/driven-adapters/mem-stash/build.gradle b/infrastructure/driven-adapters/mem-stash/build.gradle index d105405..ffd0be1 100644 --- a/infrastructure/driven-adapters/mem-stash/build.gradle +++ b/infrastructure/driven-adapters/mem-stash/build.gradle @@ -1,5 +1,5 @@ dependencies { compile project(':bin-stash-model') compile 'org.apache.commons:commons-lang3:3.11' - compile 'com.github.ben-manes.caffeine:caffeine:3.0.0' + compile 'com.github.ben-manes.caffeine:caffeine:3.0.5' } diff --git a/infrastructure/driven-adapters/mem-stash/src/main/java/co/com/bancolombia/binstash/adapter/memory/MemoryStash.java b/infrastructure/driven-adapters/mem-stash/src/main/java/co/com/bancolombia/binstash/adapter/memory/MemoryStash.java index fa57d0d..66f14d0 100644 --- a/infrastructure/driven-adapters/mem-stash/src/main/java/co/com/bancolombia/binstash/adapter/memory/MemoryStash.java +++ b/infrastructure/driven-adapters/mem-stash/src/main/java/co/com/bancolombia/binstash/adapter/memory/MemoryStash.java @@ -1,9 +1,11 @@ package co.com.bancolombia.binstash.adapter.memory; import co.com.bancolombia.binstash.model.InvalidKeyException; +import co.com.bancolombia.binstash.model.InvalidValueException; import co.com.bancolombia.binstash.model.api.Stash; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import lombok.Data; import org.apache.commons.lang3.StringUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -17,40 +19,69 @@ public class MemoryStash implements Stash { + private static final int DEFAULT_BASE_EXPIRATION_SECONDS = Integer.MAX_VALUE; + private static final int DEFAULT_PER_KEY_EXPIRATION_SECONDS = 300; private static final String ERROR_KEY_MSG = "Caching key cannot be null"; + private static final String ERROR_VALUE_MSG = "Caching empty or null value not allowed"; + private static final String KEY_SEP = "#"; - private final Cache caffeineCache; + private final Cache caffeineCache; + private final int expireAfter; - private MemoryStash(Cache caffeineCache) { + private MemoryStash(Cache caffeineCache, int expireAfter) { this.caffeineCache = caffeineCache; + this.expireAfter = expireAfter; } @Override public Mono save(String key, String value) { + return save(key, value, -1); + } + + @Override + public Mono save(String key, String value, int ttl) { return Mono.fromSupplier(() -> { if (StringUtils.isBlank(key)) throw new InvalidKeyException(ERROR_KEY_MSG); - caffeineCache.put(key, value); + caffeineCache.put(key, new Entry(value, computeTtl(ttl))); return value; }); } @Override public Mono> hSave(String key, Map value) { + return hSave(key, value, -1); + } + + public Mono> hSave(String key, Map value, int ttl) { return Mono.fromSupplier(() -> { - if (StringUtils.isBlank(key) || value == null || value.isEmpty()) + if (StringUtils.isBlank(key)) { throw new InvalidKeyException(ERROR_KEY_MSG); - value.forEach((name, item) -> caffeineCache.put(key+"-"+name, item)); + } + if (value == null || value.isEmpty()) { + throw new InvalidValueException(ERROR_VALUE_MSG); + } + value.forEach((name, item) -> caffeineCache.put(key + KEY_SEP + name, + new Entry(item, computeTtl(ttl)))); return value; }); } @Override public Mono hSave(String key, String name, String value) { + return hSave(key, name, value, -1); + } + + public Mono hSave(String key, String name, String value, int ttl) { return Mono.fromSupplier(() -> { - if (StringUtils.isAnyBlank(key, name, value)) + if (StringUtils.isAnyBlank(key, name)) { throw new InvalidKeyException(ERROR_KEY_MSG); - caffeineCache.put(key+"-"+name, value); + } + if (StringUtils.isBlank(value)) { + throw new InvalidValueException(ERROR_KEY_MSG); + } + caffeineCache.put(key + KEY_SEP + name, + new Entry(value, computeTtl(ttl))); return value; }); } @@ -61,19 +92,27 @@ public Mono get(String key) { if (StringUtils.isBlank(key)) throw new InvalidKeyException(ERROR_KEY_MSG); return caffeineCache.getIfPresent(key); - }); + }) + .filter(entry -> !entry.amIExpired(System.currentTimeMillis())) + .map(Entry::getData); } @Override public Mono> keySet() { - return Mono.just(caffeineCache.asMap().keySet()); + long currentTime = System.currentTimeMillis(); + return Mono.fromSupplier(() -> caffeineCache.asMap().entrySet() + .stream() + .filter(e -> !e.getValue().amIExpired(currentTime)) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()) + ); } @Override public Mono hGet(String key, String name) { if (StringUtils.isAnyBlank(key, name)) return Mono.error(new InvalidKeyException(ERROR_KEY_MSG)); - return this.get(key+"-"+name); + return this.get(key + KEY_SEP + name); } @Override @@ -81,17 +120,14 @@ public Mono> hGetAll(String key) { if (StringUtils.isBlank(key)) return Mono.error(new InvalidKeyException(ERROR_KEY_MSG)); else { - final String prefix = key+"-"; - return Mono.just(prefix) - .map(prefix1 -> caffeineCache.asMap().keySet().stream() - .filter(k -> k.startsWith(prefix1)) - .collect(Collectors.toList())) + final String prefix = key + KEY_SEP; + return keySet().map(keys -> keys.stream().filter(k -> k.startsWith(prefix)).collect(Collectors.toList())) .map(keys -> Tuples.of(keys, caffeineCache.getAllPresent(keys))) .map(tuple2 -> { final Map newMap = new HashMap<>(); tuple2.getT1().forEach(k -> { final String _k = k.substring(prefix.length()); - newMap.put(_k, tuple2.getT2().get(k)); + newMap.put(_k, tuple2.getT2().get(k).getData()); }); return newMap; }) @@ -120,12 +156,12 @@ public Mono evict(String key) { public Mono hDelete(String key, String name) { if (StringUtils.isAnyBlank(key, name)) return Mono.just(false); - return this.evict(key+"-"+name); + return this.evict(key + KEY_SEP + name); } @Override public Mono hDelete(String key) { - final String prefix = key+"-"; + final String prefix = key + KEY_SEP; return Mono.fromSupplier(() -> { if (StringUtils.isBlank(key)) throw new InvalidKeyException(ERROR_KEY_MSG); @@ -146,12 +182,41 @@ public Mono evictAll() { }); } + private int computeTtl(int cadidateTtl) { + int computed; + if (this.expireAfter > 0) { + computed = this.expireAfter; + } + else if (cadidateTtl > 0) { + computed = cadidateTtl; + } + else { + computed = DEFAULT_PER_KEY_EXPIRATION_SECONDS; + } + return computed; + } + + @Data + public static final class Entry { + private String data; + private long expiresAt; + + public Entry(String data, int ttlSecods) { + this.data = data; + this.expiresAt = System.currentTimeMillis() + (ttlSecods * 1_000L); + } + + public boolean amIExpired(long timestamp) { + return timestamp > this.expiresAt; + } + } + public static final class Builder { - private int expireAfter = 300; // seconds + private int expireAfter = -1; // seconds private int maxSize = 1_000; public Builder expireAfter(int seconds) { - this.expireAfter = seconds; + this.expireAfter = seconds; return this; } @@ -161,11 +226,13 @@ public Builder maxSize(int size) { } public MemoryStash build() { - Cache cacheImpl = Caffeine.newBuilder() - .maximumSize(this.maxSize) - .expireAfterWrite(this.expireAfter, TimeUnit.SECONDS) - .build(); - return new MemoryStash(cacheImpl); + return new MemoryStash( + Caffeine.newBuilder() + .maximumSize(this.maxSize) + .expireAfterWrite((this.expireAfter<=0)?DEFAULT_BASE_EXPIRATION_SECONDS:this.expireAfter, TimeUnit.SECONDS) + .build(), + this.expireAfter + ); } } } diff --git a/infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/MemoryStashTest.java b/infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/MemoryMapStashTest.java similarity index 57% rename from infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/MemoryStashTest.java rename to infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/MemoryMapStashTest.java index f9b58f8..aedaee9 100644 --- a/infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/MemoryStashTest.java +++ b/infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/MemoryMapStashTest.java @@ -8,16 +8,16 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import java.time.Duration; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertNotNull; -class MemoryStashTest { - - private static final String TEST_KEY = "key1"; - private static final String TEST_VALUE = "Hello World"; +class MemoryMapStashTest { private MemoryStash stash; private Map demoMap; @@ -36,8 +36,9 @@ void prepare() { @AfterEach void clean() { - stash.evictAll(); - stash = null; + stash.keySet().subscribe(set -> { + set.stream().forEach(key -> stash.hDelete(key).subscribe()); + }); } @Test @@ -58,16 +59,6 @@ void testCreateWithDefaults() { assertNotNull(stash2); } - @Test - @DisplayName("Should save element") - void testSave() { - StepVerifier.create(stash.save(TEST_KEY, TEST_VALUE)) - .expectSubscription() - .expectNext(TEST_VALUE) - .expectComplete() - .verify(); - } - @Test @DisplayName("Should save hash") void testSaveHash() { @@ -102,28 +93,6 @@ void testSaveHashElement() { .verify(); } - @Test - @DisplayName("Should not save with null key") - void testSaveWithNullKey() { - StepVerifier.create(stash.save(null, TEST_VALUE)) - .expectSubscription() - .expectErrorMessage("Caching key cannot be null") - .verify(); - } - - @Test - @DisplayName("Should save then get element") - void testSaveGet() { - Mono op = stash.save(TEST_KEY, TEST_VALUE) - .then(stash.get(TEST_KEY)); - - StepVerifier.create(op) - .expectSubscription() - .expectNext(TEST_VALUE) - .expectComplete() - .verify(); - } - @Test @DisplayName("Should get hash element") void testGetHashElement() { @@ -131,8 +100,8 @@ void testGetHashElement() { values.put("email", "pparker@avengers.com"); values.put("location", "Ny"); - Mono saveMono = stash.hSave("h3", values) - .then(stash.hGet("h3", "email")); + Mono saveMono = stash.hSave("h31", values) + .then(stash.hGet("h31", "email")); StepVerifier.create(saveMono) .expectSubscription() @@ -210,24 +179,6 @@ void testDeleteFieldMap() { .verify(); } - @Test - @DisplayName("Should get empty on unexistent key") - void testGetEmpty() { - StepVerifier.create(stash.get("unexistent-key")) - .expectSubscription() - .expectComplete() - .verify(); - } - - @Test - @DisplayName("Should handle get with null key") - void testGetNullKey() { - StepVerifier.create(stash.get(null)) - .expectSubscription() - .expectErrorMessage("Caching key cannot be null") - .verify(); - } - @Test @DisplayName("Should get keys") void testKeySet() { @@ -236,7 +187,7 @@ void testKeySet() { .then(stash.keySet()); List expectedKeys = demoMap.keySet().stream() - .map(key -> "h6-"+key) + .map(key -> "h6#"+key) .collect(Collectors.toList()); StepVerifier.create(keysMono) @@ -245,79 +196,4 @@ void testKeySet() { .expectComplete() .verify(); } - - @Test - @DisplayName("Should save, expire, then try to get element") - void testPutExpireGet() { - Mono op = stash.save(TEST_KEY, TEST_VALUE) - .delayElement(Duration.ofSeconds(2)) - .then(stash.get(TEST_KEY)); - - StepVerifier.create(op) - .expectSubscription() - .expectComplete() - .verify(); - } - - @Test - @DisplayName("Should save, evict, then try to get element") - void testPutEvictGet() { - Mono op = stash.save(TEST_KEY, TEST_VALUE) - .then(stash.evict(TEST_KEY)) - .then(stash.get(TEST_KEY)); - - StepVerifier.create(op) - .expectSubscription() - .expectComplete() - .verify(); - } - - @Test - @DisplayName("Should test if key exists") - void testExists() { - Mono op = stash.save(TEST_KEY, TEST_VALUE) - .then(stash.exists(TEST_KEY)); - - StepVerifier.create(op) - .expectSubscription() - .expectNext(true) - .expectComplete() - .verify(); - - StepVerifier.create(stash.exists("unexistent-key")) - .expectSubscription() - .expectNext(false) - .expectComplete() - .verify(); - - StepVerifier.create(stash.exists(null)) - .expectSubscription() - .expectErrorMessage("Caching key cannot be null") - .verify(); - } - - - @Test - @DisplayName("Should handle null key on evit") - void testEvictWithNullKey() { - StepVerifier.create(stash.evict(null)) - .expectSubscription() - .expectNext(false) - .expectComplete() - .verify(); - } - - @Test - @DisplayName("Should save keys, then evict all") - void testPutEvictAll() { - Mono op = stash.save(TEST_KEY, TEST_VALUE) - .then(stash.save(TEST_KEY+"b", TEST_VALUE)) - .then(stash.evictAll()) - .then(stash.get(TEST_KEY)); - - StepVerifier.create(op) - .expectSubscription() - .expectComplete() - .verify(); - } } diff --git a/infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/MemoryStringStashTest.java b/infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/MemoryStringStashTest.java new file mode 100644 index 0000000..b5c98bf --- /dev/null +++ b/infrastructure/driven-adapters/mem-stash/src/test/java/co/com/bancolombia/binstash/adapter/redis/MemoryStringStashTest.java @@ -0,0 +1,185 @@ +package co.com.bancolombia.binstash.adapter.redis; + +import co.com.bancolombia.binstash.adapter.memory.MemoryStash; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class MemoryStringStashTest { + + private static final String TEST_KEY = "key1"; + private static final String TEST_VALUE = "Hello World"; + + private MemoryStash stash; + private Map demoMap; + + @BeforeEach + void prepare() { + demoMap = new HashMap<>(); + demoMap.put("name", "Peter"); + demoMap.put("lastName", "Parker"); + + stash = new MemoryStash.Builder() + .expireAfter(1) + .maxSize(10) + .build(); + } + + @AfterEach + void clean() { + stash.evictAll(); + stash = null; + } + + @Test + @DisplayName("Should create instance") + void testCreate() { + MemoryStash stash2 = new MemoryStash.Builder() + .expireAfter(2) + .maxSize(10) + .build(); + assertNotNull(stash2); + } + + @Test + @DisplayName("Should create instance with defaults") + void testCreateWithDefaults() { + MemoryStash stash2 = new MemoryStash.Builder() + .build(); + assertNotNull(stash2); + } + + @Test + @DisplayName("Should save element") + void testSave() { + StepVerifier.create(stash.save(TEST_KEY, TEST_VALUE)) + .expectSubscription() + .expectNext(TEST_VALUE) + .expectComplete() + .verify(); + } + + @Test + @DisplayName("Should not save with null key") + void testSaveWithNullKey() { + StepVerifier.create(stash.save(null, TEST_VALUE)) + .expectSubscription() + .expectErrorMessage("Caching key cannot be null") + .verify(); + } + + @Test + @DisplayName("Should save then get element") + void testSaveGet() { + Mono op = stash.save(TEST_KEY, TEST_VALUE) + .then(stash.get(TEST_KEY)); + + StepVerifier.create(op) + .expectSubscription() + .expectNext(TEST_VALUE) + .expectComplete() + .verify(); + } + + @Test + @DisplayName("Should get empty on unexistent key") + void testGetEmpty() { + StepVerifier.create(stash.get("unexistent-key")) + .expectSubscription() + .expectComplete() + .verify(); + } + + @Test + @DisplayName("Should handle get with null key") + void testGetNullKey() { + StepVerifier.create(stash.get(null)) + .expectSubscription() + .expectErrorMessage("Caching key cannot be null") + .verify(); + } + + @Test + @DisplayName("Should save, expire, then try to get element") + void testPutExpireGet() { + Mono op = stash.save(TEST_KEY, TEST_VALUE) + .delayElement(Duration.ofSeconds(2)) + .then(stash.get(TEST_KEY)); + + StepVerifier.create(op) + .expectSubscription() + .expectComplete() + .verify(); + } + + @Test + @DisplayName("Should save, evict, then try to get element") + void testPutEvictGet() { + Mono op = stash.save(TEST_KEY, TEST_VALUE) + .then(stash.evict(TEST_KEY)) + .then(stash.get(TEST_KEY)); + + StepVerifier.create(op) + .expectSubscription() + .expectComplete() + .verify(); + } + + @Test + @DisplayName("Should test if key exists") + void testExists() { + Mono op = stash.save(TEST_KEY, TEST_VALUE) + .then(stash.exists(TEST_KEY)); + + StepVerifier.create(op) + .expectSubscription() + .expectNext(true) + .expectComplete() + .verify(); + + StepVerifier.create(stash.exists("unexistent-key")) + .expectSubscription() + .expectNext(false) + .expectComplete() + .verify(); + + StepVerifier.create(stash.exists(null)) + .expectSubscription() + .expectErrorMessage("Caching key cannot be null") + .verify(); + } + + + @Test + @DisplayName("Should handle null key on evit") + void testEvictWithNullKey() { + StepVerifier.create(stash.evict(null)) + .expectSubscription() + .expectNext(false) + .expectComplete() + .verify(); + } + + @Test + @DisplayName("Should save keys, then evict all") + void testPutEvictAll() { + Mono op = stash.save(TEST_KEY, TEST_VALUE) + .then(stash.save(TEST_KEY+"b", TEST_VALUE)) + .then(stash.evictAll()) + .then(stash.get(TEST_KEY)); + + StepVerifier.create(op) + .expectSubscription() + .expectComplete() + .verify(); + } +} diff --git a/infrastructure/driven-adapters/redis-stash/src/main/java/co/com/bancolombia/binstash/adapter/redis/RedisStash.java b/infrastructure/driven-adapters/redis-stash/src/main/java/co/com/bancolombia/binstash/adapter/redis/RedisStash.java index f0730b3..4383654 100644 --- a/infrastructure/driven-adapters/redis-stash/src/main/java/co/com/bancolombia/binstash/adapter/redis/RedisStash.java +++ b/infrastructure/driven-adapters/redis-stash/src/main/java/co/com/bancolombia/binstash/adapter/redis/RedisStash.java @@ -16,6 +16,7 @@ public class RedisStash implements Stash { private static final String ERROR_KEY_MSG = "Caching key cannot be null"; + private static final int DEFAULT_PER_KEY_EXPIRATION_SECONDS = 300; private final RedisReactiveCommands redisReactiveCommands; private final int expireAfter; @@ -27,15 +28,20 @@ private RedisStash(RedisReactiveCommands redisReactiveCommands, } @Override - public Mono save(String key, String value) { + public Mono save(String key, String value, int ttl) { if (StringUtils.isAnyBlank(key, value)) { return Mono.error(new InvalidKeyException(ERROR_KEY_MSG)); } else { - return redisReactiveCommands.set(key, value, SetArgs.Builder.ex(this.expireAfter)) + return redisReactiveCommands.set(key, value, SetArgs.Builder.ex(computeTtl(ttl))) .map(r -> value); } } + @Override + public Mono save(String key, String value) { + return save(key, value, this.expireAfter); // with default expire ttl + } + @Override public Mono get(String key) { if (StringUtils.isBlank(key)) { @@ -76,20 +82,32 @@ public Mono evictAll() { @Override public Mono> hSave(String key, Map value) { + return hSave(key, value, DEFAULT_PER_KEY_EXPIRATION_SECONDS); + } + + @Override + public Mono> hSave(String key, Map value, int ttl) { if (StringUtils.isBlank(key) || value == null) { return Mono.error(new InvalidKeyException(ERROR_KEY_MSG)); } else { return redisReactiveCommands.hmset(key, value) + .zipWith(redisReactiveCommands.expire(key, computeTtl(ttl))) .map(r -> value); } } @Override public Mono hSave(String key, String field, String value) { + return hSave(key, field, value, DEFAULT_PER_KEY_EXPIRATION_SECONDS); + } + + @Override + public Mono hSave(String key, String field, String value, int ttl) { if (StringUtils.isAnyBlank(key, field, value)) { return Mono.error(new InvalidKeyException(ERROR_KEY_MSG)); } else { return redisReactiveCommands.hset(key, field, value) + .zipWith(redisReactiveCommands.expire(key, computeTtl(ttl))) .map(r -> value); } } @@ -135,12 +153,26 @@ public Mono hDelete(String key) { } } + private int computeTtl(int cadidateTtl) { + int computed; + if (cadidateTtl > 0) { + computed = cadidateTtl; + } + else if (cadidateTtl < 0 && this.expireAfter > 0) { + computed = this.expireAfter; + } + else { + computed = DEFAULT_PER_KEY_EXPIRATION_SECONDS; + } + return computed; + } + public static final class Builder { private String host; private int port = 6379; private int database = 0; private String password; - private int expireAfter = 300; // seconds + private int expireAfter = -1; public Builder expireAfter(int seconds) { this.expireAfter = seconds; diff --git a/main.gradle b/main.gradle index a3d3fa6..b6e2ece 100644 --- a/main.gradle +++ b/main.gradle @@ -54,6 +54,14 @@ subprojects { testImplementation 'org.mockito:mockito-junit-jupiter:3.7.7' } + configurations.all { + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + if (details.requested.group == 'org.apache.logging.log4j') { + details.useVersion '2.17.1' + } + } + } + dependencyManagement { imports { mavenBom "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"