Skip to content

Commit

Permalink
add ttl&set reg&res logic for jdempotent cb starter
Browse files Browse the repository at this point in the history
  • Loading branch information
memojja committed Jul 16, 2021
1 parent 790b1d0 commit 525215a
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 62 deletions.
2 changes: 1 addition & 1 deletion Jdempotent-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.trendyol</groupId>
<artifactId>Jdempotent-core</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
<name>Jdempotent-core</name>
<packaging>jar</packaging>
<url>https://github.com/Trendyol/Jdempotent/tree/master/Jdempotent-core</url>
Expand Down
4 changes: 2 additions & 2 deletions Jdempotent-spring-boot-couchbase-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.trendyol</groupId>
<artifactId>Jdempotent-spring-boot-couchbase-starter</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
<name>Jdempotent-spring-boot-couchbase-starter</name>
<packaging>jar</packaging>
<url>https://github.com/Trendyol/Jdempotent/tree/master/Jdempotent-spring-boot-couchbase-starter</url>
Expand Down Expand Up @@ -54,7 +54,7 @@
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>Jdempotent-core</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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
Expand All @@ -20,10 +25,12 @@
public class CouchbaseIdempotentRepository implements IdempotentRepository {
private final CouchbaseConfig couchbaseConfig;
private final Collection collection;
private Map<TimeUnit, Function<Long, Duration>> ttlConverter = new HashMap<>();

public CouchbaseIdempotentRepository(CouchbaseConfig couchbaseConfig, Collection collection) {
this.couchbaseConfig = couchbaseConfig;
this.collection = collection;
this.prepareTtlConverter();
}


Expand All @@ -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)
);
}
Expand All @@ -59,28 +66,67 @@ 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);
}
}

@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;
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
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;
import org.mockito.ArgumentCaptor;
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;
Expand Down Expand Up @@ -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());
}

Expand All @@ -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);
Expand All @@ -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());
}
}
4 changes: 2 additions & 2 deletions Jdempotent-spring-boot-redis-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.trendyol</groupId>
<artifactId>Jdempotent-spring-boot-redis-starter</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
<name>Jdempotent-spring-boot-redis-starter</name>
<packaging>jar</packaging>
<url>https://github.com/Trendyol/Jdempotent/tree/master/Jdempotent-spring-boot-redis-starter</url>
Expand Down Expand Up @@ -54,7 +54,7 @@
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>Jdempotent-core</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
Expand Down
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>Jdempotent-spring-boot-redis-starter</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
</dependency>
```
For Couchbase
```xml
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>Jdempotent-spring-boot-couchbase-starter</artifactId>
<version>1.0.6</version>
</dependency>
```

2 - You should add `@IdempotentResource` annotation to that you want to make idempotent resource, listener etc.

Expand Down Expand Up @@ -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) <br/>
[Jdempotent-core Javadoc](https://memojja.github.io/jdempotent-core/index.html) <br/>
[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)
2 changes: 1 addition & 1 deletion examples/jdempotent-couchbase-example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>Jdempotent-spring-boot-couchbase-starter</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
</dependency>
</dependencies>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ jdempotent:
cryptography:
algorithm: MD5
cache:
persistReqRes: false
couchbase:
connection-string: XXXXXXXX
password: XXXXXXXX
Expand Down
2 changes: 1 addition & 1 deletion examples/jdempotent-redis-example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>Jdempotent-spring-boot-redis-starter</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
</dependency>
</dependencies>

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<groupId>com.trendyol</groupId>
<artifactId>jdempotent</artifactId>
<packaging>pom</packaging>
<version>1.0.5</version>
<version>1.0.6</version>
<name>Jdempotent</name>
<url>https://github.com/Trendyol/Jdempotent</url>
<description>Jdempotent</description>
Expand Down

0 comments on commit 525215a

Please sign in to comment.