Make your endpoints idempotent easily
1 - First of all, you need to add a dependency to pom.xml
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>Jdempotent-spring-boot-redis-starter</artifactId>
<version>1.0.4</version>
</dependency>
2 - You should add @IdempotentResource
annotation to the method that you want to make idempotent resource, listener etc.
@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);
}
}
3 - If you want to handle a custom error case, you need to implement ErrorConditionalCallback
like the following example:
@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 redis configuration:
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
Please note that you can disable Jdempotent easily if you need to. For example, assume that you don't have a circut breaker and your Redis is down. In that case, you can disable Jdempotent with the following configuration:
enable: false
@SpringBootApplication(
exclude = { RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class }
)
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.
Jdempotent Medium Article
Jdempotent-core Javadoc
Jdempotent-spring-boot-redis-starter Javadoc
- 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