https://github.com/trendyol/jdempotent
Make your consumer, API, etc. idempotent easily.
https://github.com/trendyol/jdempotent
couchbase idempotency idempotent-consumer idempotent-kafka idempotent-rabbitmq redis transactional-consumer
Last synced: about 1 year ago
JSON representation
Make your consumer, API, etc. idempotent easily.
- Host: GitHub
- URL: https://github.com/trendyol/jdempotent
- Owner: Trendyol
- License: mit
- Created: 2020-11-19T16:44:30.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2025-02-03T11:18:19.000Z (over 1 year ago)
- Last Synced: 2025-04-05T01:05:52.358Z (about 1 year ago)
- Topics: couchbase, idempotency, idempotent-consumer, idempotent-kafka, idempotent-rabbitmq, redis, transactional-consumer
- Language: Java
- Homepage:
- Size: 962 KB
- Stars: 100
- Watchers: 12
- Forks: 25
- Open Issues: 16
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Jdempotent
[](https://github.com/Trendyol/Jdempotent/actions/workflows/jdempotent-spring-boot-redis-starter.yml)
# Goal of this Jdempotent-spring-boot-starter
Make your endpoints idempotent easily
# Usage
1. First of all, you need to add a dependency to pom.xml
For Redis:
```xml
com.trendyol
Jdempotent-spring-boot-redis-starter
1.1.0
```
For Couchbase:
```xml
com.trendyol
Jdempotent-spring-boot-couchbase-starter
1.1.0
```
2. You should add `@IdempotentResource` annotation to the method that you want to make idempotent resource, listener etc.
```java
@IdempotentResource(cachePrefix = "WelcomingListener")
@KafkaListener(topics = "trendyol.mail.welcome", groupId = "group_id")
public void consumeMessage(@IdempotentRequestPayload String emailAdress) {
SendEmailRequest request = SendEmailRequest.builder()
.email(message)
.subject(subject)
.build();
try {
mailSenderService.sendMail(request);
} catch (MessagingException e) {
logger.error("MailSenderService.sendEmail() throw exception {} event: {} ", e, emailAdress);
// Throwing any exception is enough to delete from redis. When successful, it will not be deleted from redis and will be idempotent.
throw new RetryIdempotentRequestException(e);
}
}
```
If want that idempotencyId in your payload. Put `@JdempotentId` annotation that places the generated idempotency identifier into annotated field.
Can be thought of as @Id annotation in jpa.
```java
public class IdempotentPayload {
@JdempotentId
private String jdempotentId;
private Object data;
}
```
You might want to handle the name of the field differently to ensure idempotency. Just use @JdempotentProperty annotation needs to get the field name differently and generate the hash inspired by jackson (@JsonProperty annotation)
```java
public class IdempotentPayload {
@JdempotentProperty("userId")
private String customerId;
private Object data;
}
```
3. If you want to handle a custom error case, you need to implement `ErrorConditionalCallback` like the following example:
```java
@Component
public class AspectConditionalCallback implements ErrorConditionalCallback {
@Override
public boolean onErrorCondition(Object response) {
return response == IdempotentStateEnum.ERROR;
}
public RuntimeException onErrorCustomException() {
return new RuntimeException("Status cannot be error");
}
}
```
4. Let's make the configuration:
For redis configuration:
```yaml
jdempotent:
enable: true
cache:
redis:
database: 1
password: "password"
sentinelHostList: 192.168.0.1,192.168.0.2,192.168.0.3
sentinelPort: "26379"
sentinelMasterName: "admin"
expirationTimeHour: 2
dialTimeoutSecond: 3
readTimeoutSecond: 3
writeTimeoutSecond: 3
maxRetryCount: 3
expireTimeoutHour: 3
```
For couchbase configuration:
```yaml
jdempotent:
enable: true
cryptography:
algorithm: MD5
cache:
couchbase:
connection-string: XXXXXXXX
password: XXXXXXXX
username: XXXXXXXX
bucket-name: XXXXXXXX
connect-timeout: 100000
query-timeout: 20000
kv-timeout: 3000
```
Please note that you can disable Jdempotent easily if you need to.
For example, assume that you don't have a circuit breaker and your Redis is down.
In that case, you can disable Jdempotent with the following configuration:
```yaml
enable: false
```
```java
@SpringBootApplication(
exclude = { RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class }
)
```
## Performance
As it is shown in the following image, the most cpu consuming part of Jdempotent is getting a Redis connection so we don't need to worry performance related issues.
# 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)
## Support
[memojja's twitter](https://twitter.com/memojja)
## Licence
[MIT Licence](https://opensource.org/licenses/MIT)
## Contributing
1. Fork it ( https://github.com/Trendyol/Jdempotent/fork )
2. Create your feature branch (git checkout -b my-new-feature)
3. Commit your changes (git commit -am 'Add some feature')
4. Push to the branch (git push origin my-new-feature)
5. Create a new Pull Request
## Contributors
- [memojja](https://github.com/memojja) Mehmet ARI - creator, maintainer