From 525215a04180b914e1853c938f90b01e74d777f6 Mon Sep 17 00:00:00 2001 From: memojja Date: Fri, 16 Jul 2021 12:08:09 +0300 Subject: [PATCH] add ttl&set reg&res logic for jdempotent cb starter --- Jdempotent-core/pom.xml | 2 +- .../pom.xml | 4 +- .../jdempotent/couchbase/CouchbaseConfig.java | 23 +----- .../CouchbaseIdempotentRepository.java | 74 +++++++++++++++---- .../CouchbaseIdempotentRepositoryTest.java | 58 ++++++++++++--- Jdempotent-spring-boot-redis-starter/pom.xml | 4 +- README.md | 20 ++--- examples/jdempotent-couchbase-example/pom.xml | 2 +- .../src/main/resources/application.yml | 1 + examples/jdempotent-redis-example/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 130 insertions(+), 62 deletions(-) rename Jdempotent-spring-boot-couchbase-starter/src/test/java/{com.trendyol.jtempotent.couchbase => com/trendyol/jdempotent/couchbase}/CouchbaseIdempotentRepositoryTest.java (67%) diff --git a/Jdempotent-core/pom.xml b/Jdempotent-core/pom.xml index 1c96a26..2e6169e 100644 --- a/Jdempotent-core/pom.xml +++ b/Jdempotent-core/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.trendyol Jdempotent-core - 1.0.5 + 1.0.6 Jdempotent-core jar https://github.com/Trendyol/Jdempotent/tree/master/Jdempotent-core diff --git a/Jdempotent-spring-boot-couchbase-starter/pom.xml b/Jdempotent-spring-boot-couchbase-starter/pom.xml index 15eacc2..1284db5 100644 --- a/Jdempotent-spring-boot-couchbase-starter/pom.xml +++ b/Jdempotent-spring-boot-couchbase-starter/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.trendyol Jdempotent-spring-boot-couchbase-starter - 1.0.5 + 1.0.6 Jdempotent-spring-boot-couchbase-starter jar https://github.com/Trendyol/Jdempotent/tree/master/Jdempotent-spring-boot-couchbase-starter @@ -54,7 +54,7 @@ com.trendyol Jdempotent-core - 1.0.5 + 1.0.6 org.aspectj diff --git a/Jdempotent-spring-boot-couchbase-starter/src/main/java/com/trendyol/jdempotent/couchbase/CouchbaseConfig.java b/Jdempotent-spring-boot-couchbase-starter/src/main/java/com/trendyol/jdempotent/couchbase/CouchbaseConfig.java index dac5617..03f4d16 100644 --- a/Jdempotent-spring-boot-couchbase-starter/src/main/java/com/trendyol/jdempotent/couchbase/CouchbaseConfig.java +++ b/Jdempotent-spring-boot-couchbase-starter/src/main/java/com/trendyol/jdempotent/couchbase/CouchbaseConfig.java @@ -1,29 +1,8 @@ package com.trendyol.jdempotent.couchbase; -import com.couchbase.client.core.deps.io.netty.channel.epoll.EpollEventLoopGroup; -import com.couchbase.client.core.env.CompressionConfig; -import com.couchbase.client.core.env.IoConfig; -import com.couchbase.client.core.env.IoEnvironment; -import com.couchbase.client.core.env.LoggerConfig; -import com.couchbase.client.core.env.SecurityConfig; -import com.couchbase.client.core.env.TimeoutConfig; -import com.couchbase.client.java.Cluster; -import com.couchbase.client.java.ClusterOptions; -import com.couchbase.client.java.Collection; -import com.couchbase.client.java.codec.JacksonJsonSerializer; -import com.couchbase.client.java.env.ClusterEnvironment; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.time.Duration; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.AutoConfigurationPackage; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.apache.commons.lang3.SystemUtils; -import org.springframework.context.annotation.Primary; @ConditionalOnProperty( prefix="jdempotent", name = "enable", @@ -45,7 +24,7 @@ public class CouchbaseConfig { private Long queryTimeout; @Value("${jdempotent.cache.couchbase.kv-timeout}") private Long kvTimeout; - @Value("${jdempotent.cache.persistReqRes:true}") + @Value("${jdempotent.cache.persistReqRes:false}") private Boolean persistReqRes; public String getConnectionString() { diff --git a/Jdempotent-spring-boot-couchbase-starter/src/main/java/com/trendyol/jdempotent/couchbase/CouchbaseIdempotentRepository.java b/Jdempotent-spring-boot-couchbase-starter/src/main/java/com/trendyol/jdempotent/couchbase/CouchbaseIdempotentRepository.java index 7db2a4d..654a78e 100644 --- a/Jdempotent-spring-boot-couchbase-starter/src/main/java/com/trendyol/jdempotent/couchbase/CouchbaseIdempotentRepository.java +++ b/Jdempotent-spring-boot-couchbase-starter/src/main/java/com/trendyol/jdempotent/couchbase/CouchbaseIdempotentRepository.java @@ -1,6 +1,8 @@ package com.trendyol.jdempotent.couchbase; import com.couchbase.client.java.Collection; +import com.couchbase.client.java.kv.GetOptions; +import com.couchbase.client.java.kv.GetResult; import com.couchbase.client.java.kv.UpsertOptions; import com.trendyol.jdempotent.core.datasource.IdempotentRepository; import com.trendyol.jdempotent.core.model.IdempotencyKey; @@ -9,7 +11,10 @@ import com.trendyol.jdempotent.core.model.IdempotentResponseWrapper; import java.time.Duration; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.function.Function; /** * An implementation of the idempotent IdempotentRepository @@ -20,10 +25,12 @@ public class CouchbaseIdempotentRepository implements IdempotentRepository { private final CouchbaseConfig couchbaseConfig; private final Collection collection; + private Map> ttlConverter = new HashMap<>(); public CouchbaseIdempotentRepository(CouchbaseConfig couchbaseConfig, Collection collection) { this.couchbaseConfig = couchbaseConfig; this.collection = collection; + this.prepareTtlConverter(); } @@ -34,19 +41,19 @@ public boolean contains(IdempotencyKey key) { @Override public IdempotentResponseWrapper getResponse(IdempotencyKey key) { - return collection.get(key.getKeyValue()).contentAs(IdempotentRequestResponseWrapper.class).getResponse(); + return collection.get(key.getKeyValue(), GetOptions.getOptions().withExpiry(true)).contentAs(IdempotentRequestResponseWrapper.class).getResponse(); } @Override public void store(IdempotencyKey key, IdempotentRequestWrapper requestObject) { - collection.insert(key.getKeyValue(),new IdempotentRequestResponseWrapper(requestObject)); + collection.insert(key.getKeyValue(), prepareRequestValue(requestObject)); } @Override public void store(IdempotencyKey key, IdempotentRequestWrapper requestObject, Long ttl, TimeUnit timeUnit) { Duration ttlDuration = getDurationByTttlAndTimeUnit(ttl, timeUnit); collection.upsert( - key.getKeyValue(), new IdempotentRequestResponseWrapper(requestObject), + key.getKeyValue(), prepareRequestValue(requestObject), UpsertOptions.upsertOptions().expiry(ttlDuration) ); } @@ -59,8 +66,8 @@ public void remove(IdempotencyKey key) { @Override public void setResponse(IdempotencyKey key, IdempotentRequestWrapper request, IdempotentResponseWrapper idempotentResponse) { if (contains(key)) { - IdempotentRequestResponseWrapper requestResponseWrapper = collection.get(key.getKeyValue()).contentAs(IdempotentRequestResponseWrapper.class); - requestResponseWrapper.setResponse(idempotentResponse); + GetResult getResult = collection.get(key.getKeyValue(), GetOptions.getOptions().withExpiry(true)); + IdempotentRequestResponseWrapper requestResponseWrapper = prepareResponseValue(getResult,idempotentResponse); collection.upsert(key.getKeyValue(), requestResponseWrapper); } } @@ -68,19 +75,58 @@ public void setResponse(IdempotencyKey key, IdempotentRequestWrapper request, Id @Override public void setResponse(IdempotencyKey key, IdempotentRequestWrapper request, IdempotentResponseWrapper idempotentResponse, Long ttl, TimeUnit timeUnit) { if (contains(key)) { - IdempotentRequestResponseWrapper requestResponseWrapper = collection.get(key.getKeyValue()).contentAs(IdempotentRequestResponseWrapper.class); - requestResponseWrapper.setResponse(idempotentResponse); - collection.upsert(key.getKeyValue(), requestResponseWrapper); + GetResult getResult = collection.get(key.getKeyValue(),GetOptions.getOptions().withExpiry(true)); + IdempotentRequestResponseWrapper requestResponseWrapper = prepareResponseValue(getResult,idempotentResponse); + collection.upsert( + key.getKeyValue(), + requestResponseWrapper, + UpsertOptions.upsertOptions().expiry(getResult.expiry().get())); } } private Duration getDurationByTttlAndTimeUnit(Long ttl, TimeUnit timeUnit) { - if (TimeUnit.DAYS.equals(timeUnit)) { - return Duration.ofDays(ttl); - } else if (TimeUnit.HOURS.equals(timeUnit)) { - return Duration.ofHours(ttl); - } else { //TODO look here - return Duration.ofMillis(ttl); + return ttlConverter.get(timeUnit).apply(ttl); + } + + private void prepareTtlConverter() { + ttlConverter.put(TimeUnit.DAYS, Duration::ofDays); + ttlConverter.put(TimeUnit.HOURS, Duration::ofHours); + ttlConverter.put(TimeUnit.MINUTES, Duration::ofMinutes); + ttlConverter.put(TimeUnit.SECONDS, Duration::ofSeconds); + ttlConverter.put(TimeUnit.MILLISECONDS, Duration::ofMillis); + ttlConverter.put(TimeUnit.MICROSECONDS, Duration::ofMillis); + ttlConverter.put(TimeUnit.NANOSECONDS, Duration::ofNanos); + } + + /** + * Prepares the request value stored in couchbase + * + * if persistReqRes set to false, + * it does not persist related request and response values in couchbase + * @param request + * @return + */ + private IdempotentRequestResponseWrapper prepareRequestValue(IdempotentRequestWrapper request) { + if (couchbaseConfig.getPersistReqRes()) { + return new IdempotentRequestResponseWrapper(request); + } + return new IdempotentRequestResponseWrapper(null); + } + + /** + * Prepares the response value stored in couchbase + * + * if persistReqRes set to false, + * it does not persist related request and response values in redis + * @param result + * @param idempotentResponse + * @return + */ + private IdempotentRequestResponseWrapper prepareResponseValue(GetResult result,IdempotentResponseWrapper idempotentResponse) { + IdempotentRequestResponseWrapper requestResponseWrapper = result.contentAs(IdempotentRequestResponseWrapper.class); + if (couchbaseConfig.getPersistReqRes()) { + requestResponseWrapper.setResponse(idempotentResponse); } + return requestResponseWrapper; } } diff --git a/Jdempotent-spring-boot-couchbase-starter/src/test/java/com.trendyol.jtempotent.couchbase/CouchbaseIdempotentRepositoryTest.java b/Jdempotent-spring-boot-couchbase-starter/src/test/java/com/trendyol/jdempotent/couchbase/CouchbaseIdempotentRepositoryTest.java similarity index 67% rename from Jdempotent-spring-boot-couchbase-starter/src/test/java/com.trendyol.jtempotent.couchbase/CouchbaseIdempotentRepositoryTest.java rename to Jdempotent-spring-boot-couchbase-starter/src/test/java/com/trendyol/jdempotent/couchbase/CouchbaseIdempotentRepositoryTest.java index 9bbfc6b..ac622c2 100644 --- a/Jdempotent-spring-boot-couchbase-starter/src/test/java/com.trendyol.jtempotent.couchbase/CouchbaseIdempotentRepositoryTest.java +++ b/Jdempotent-spring-boot-couchbase-starter/src/test/java/com/trendyol/jdempotent/couchbase/CouchbaseIdempotentRepositoryTest.java @@ -1,13 +1,14 @@ +package com.trendyol.jdempotent.couchbase; + import com.couchbase.client.java.Collection; import com.couchbase.client.java.kv.ExistsResult; import com.couchbase.client.java.kv.GetResult; +import com.couchbase.client.java.kv.MutationResult; import com.couchbase.client.java.kv.UpsertOptions; import com.trendyol.jdempotent.core.model.IdempotencyKey; import com.trendyol.jdempotent.core.model.IdempotentRequestResponseWrapper; import com.trendyol.jdempotent.core.model.IdempotentRequestWrapper; import com.trendyol.jdempotent.core.model.IdempotentResponseWrapper; -import com.trendyol.jdempotent.couchbase.CouchbaseConfig; -import com.trendyol.jdempotent.couchbase.CouchbaseIdempotentRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -15,10 +16,10 @@ import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.time.Duration; +import java.util.Optional; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertFalse; @@ -88,13 +89,13 @@ public void given_an_available_object_when_couchbase_get_response_then_return_ex IdempotentRequestResponseWrapper wrapper = new IdempotentRequestResponseWrapper(); GetResult getResult = mock(GetResult.class); when(getResult.contentAs(IdempotentRequestResponseWrapper.class)).thenReturn(wrapper); - when(collection.get(idempotencyKey.getKeyValue())).thenReturn(getResult); + when(collection.get(eq(idempotencyKey.getKeyValue()),any())).thenReturn(getResult); //When IdempotentResponseWrapper result = couchbaseIdempotentRepository.getResponse(idempotencyKey); //Then - verify(collection, times(1)).get(idempotencyKey.getKeyValue()); + verify(collection, times(1)).get(eq(idempotencyKey.getKeyValue()),any()); assertEquals(result, wrapper.getResponse()); } @@ -121,10 +122,7 @@ public void given_an_available_object_when_couchbase_store_with_ttl_and_time_uni IdempotentRequestWrapper wrapper = new IdempotentRequestWrapper(); Long ttl = 1L; TimeUnit timeUnit = TimeUnit.DAYS; - Duration duration = Duration.ofDays(1L); IdempotentRequestResponseWrapper responseWrapper = new IdempotentRequestResponseWrapper(wrapper); - /*var mockUpsertOption = Mockito.mockStatic(UpsertOptions.class); - when(((UpsertOptions)mockUpsertOption)).thenReturn((UpsertOptions) mockUpsertOption);*/ //When couchbaseIdempotentRepository.store(idempotencyKey, wrapper, ttl, timeUnit); @@ -135,6 +133,48 @@ public void given_an_available_object_when_couchbase_store_with_ttl_and_time_uni upsertOptionCaptor.capture()); IdempotentRequestResponseWrapper idempotentRequestResponseWrapper = captor.getValue(); assertEquals(idempotentRequestResponseWrapper.getResponse(), responseWrapper.getResponse()); - //verify((UpsertOptions)mockUpsertOption, times(1)).expiry(duration); + } + + + @Test + public void setResponse() { + //Given + IdempotencyKey idempotencyKey = new IdempotencyKey("key"); + IdempotentRequestResponseWrapper wrapper = new IdempotentRequestResponseWrapper(); + GetResult getResult = mock(GetResult.class); + ExistsResult existsResult = mock(ExistsResult.class); + when(existsResult.exists()).thenReturn(true); + when(getResult.contentAs(IdempotentRequestResponseWrapper.class)).thenReturn(wrapper); + + when(collection.get(eq(idempotencyKey.getKeyValue()),any())).thenReturn(getResult); + when(collection.exists(idempotencyKey.getKeyValue())).thenReturn(existsResult); + when(collection.upsert(idempotencyKey.getKeyValue(),wrapper)).thenReturn(mock(MutationResult.class)); + //When + couchbaseIdempotentRepository.setResponse(idempotencyKey,mock(IdempotentRequestWrapper.class), + mock(IdempotentResponseWrapper.class)); + + //Then + verify(collection, times(1)).get(eq(idempotencyKey.getKeyValue()),any()); + } + + @Test + public void setResponse_when_given_a_ttl() { + //Given + IdempotencyKey idempotencyKey = new IdempotencyKey("key"); + IdempotentRequestResponseWrapper wrapper = new IdempotentRequestResponseWrapper(); + GetResult getResult = mock(GetResult.class); + ExistsResult existsResult = mock(ExistsResult.class); + when(existsResult.exists()).thenReturn(true); + when(getResult.contentAs(IdempotentRequestResponseWrapper.class)).thenReturn(wrapper); + when(getResult.expiry()).thenReturn(Optional.of(mock(Duration.class))); + when(collection.get(eq(idempotencyKey.getKeyValue()),any())).thenReturn(getResult); + when(collection.exists(idempotencyKey.getKeyValue())).thenReturn(existsResult); + when(collection.upsert(eq(idempotencyKey.getKeyValue()),eq(wrapper),any())).thenReturn(mock(MutationResult.class)); + //When + couchbaseIdempotentRepository.setResponse(idempotencyKey,mock(IdempotentRequestWrapper.class), + mock(IdempotentResponseWrapper.class),5L,TimeUnit.DAYS); + + //Then + verify(collection, times(1)).get(eq(idempotencyKey.getKeyValue()),any()); } } \ No newline at end of file diff --git a/Jdempotent-spring-boot-redis-starter/pom.xml b/Jdempotent-spring-boot-redis-starter/pom.xml index 08cf7fb..fc3e1f6 100644 --- a/Jdempotent-spring-boot-redis-starter/pom.xml +++ b/Jdempotent-spring-boot-redis-starter/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.trendyol Jdempotent-spring-boot-redis-starter - 1.0.5 + 1.0.6 Jdempotent-spring-boot-redis-starter jar https://github.com/Trendyol/Jdempotent/tree/master/Jdempotent-spring-boot-redis-starter @@ -54,7 +54,7 @@ com.trendyol Jdempotent-core - 1.0.5 + 1.0.6 redis.clients diff --git a/README.md b/README.md index 79be448..aa8ad81 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,22 @@ Make your listener or etc idempotent easily 1 - First of all you need add dependency to pom.xml +For Redis: ```xml com.trendyol Jdempotent-spring-boot-redis-starter - 1.0.5 + 1.0.6 ``` +For Couchbase +```xml + + com.trendyol + Jdempotent-spring-boot-couchbase-starter + 1.0.6 + +``` 2 - You should add `@IdempotentResource` annotation to that you want to make idempotent resource, listener etc. @@ -124,11 +133,4 @@ As it is shown in the following image, the most cpu consuming part of jdempotent ### Docs [Jdempotent Medium Article](https://medium.com/trendyol-tech/an-idempotency-library-jdempotent-5cd2cd0b76ff)
[Jdempotent-core Javadoc](https://memojja.github.io/jdempotent-core/index.html)
-[Jdempotent-spring-boot-redis-starter Javadoc](https://memojja.github.io/jdempotent-spring-boot-redis-starter/index.html) - -### TODOS -- [ ] Disable request&response configgi -- [ ] Write examples under the examples folders -- [ ] Support multiple request paylaod as a paramater -- [ ] Ignore a throwing custom exception like ErrorConditionalCallback -- [ ] Support multiple datasources +[Jdempotent-spring-boot-redis-starter Javadoc](https://memojja.github.io/jdempotent-spring-boot-redis-starter/index.html) \ No newline at end of file diff --git a/examples/jdempotent-couchbase-example/pom.xml b/examples/jdempotent-couchbase-example/pom.xml index e7b71db..788b3c3 100644 --- a/examples/jdempotent-couchbase-example/pom.xml +++ b/examples/jdempotent-couchbase-example/pom.xml @@ -59,7 +59,7 @@ com.trendyol Jdempotent-spring-boot-couchbase-starter - 1.0.5 + 1.0.6 diff --git a/examples/jdempotent-couchbase-example/src/main/resources/application.yml b/examples/jdempotent-couchbase-example/src/main/resources/application.yml index 87bce89..40a6d3a 100644 --- a/examples/jdempotent-couchbase-example/src/main/resources/application.yml +++ b/examples/jdempotent-couchbase-example/src/main/resources/application.yml @@ -3,6 +3,7 @@ jdempotent: cryptography: algorithm: MD5 cache: + persistReqRes: false couchbase: connection-string: XXXXXXXX password: XXXXXXXX diff --git a/examples/jdempotent-redis-example/pom.xml b/examples/jdempotent-redis-example/pom.xml index 145a470..0218ada 100644 --- a/examples/jdempotent-redis-example/pom.xml +++ b/examples/jdempotent-redis-example/pom.xml @@ -59,7 +59,7 @@ com.trendyol Jdempotent-spring-boot-redis-starter - 1.0.5 + 1.0.6 diff --git a/pom.xml b/pom.xml index be72e0d..6a72864 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.trendyol jdempotent pom - 1.0.5 + 1.0.6 Jdempotent https://github.com/Trendyol/Jdempotent Jdempotent