π Spring Boot starter for idempotent request handling with Redis support
English | Π ΡΡΡΠΊΠΈΠΉ
- Simple annotation
@Idempotentfor methods - SpEL expressions for key generation
- Configurable TTL per method
- Automatic key removal on error (retry-friendly)
- Full integration with Spring Boot 3.x
<dependency>
<groupId>io.github.nimv1</groupId>
<artifactId>spring-boot-starter-idempotency</artifactId>
<version>1.0.0</version>
</dependency>spring:
data:
redis:
host: localhost
port: 6379@RestController
@RequestMapping("/api/payments")
public class PaymentController {
@PostMapping
@Idempotent(key = "#request.transactionId", ttl = 24, timeUnit = TimeUnit.HOURS)
public PaymentResponse processPayment(@RequestBody PaymentRequest request) {
// This method will be executed only once for each transactionId
return paymentService.process(request);
}
}| Parameter | Type | Default | Description |
|---|---|---|---|
key |
String | required | SpEL expression for key generation |
ttl |
long | 1 | Key time-to-live |
timeUnit |
TimeUnit | HOURS | Time unit for TTL |
prefix |
String | "idempotent:" | Key prefix in Redis |
message |
String | "Duplicate request detected" | Error message |
// Parameter by name
@Idempotent(key = "#transactionId")
// Nested property
@Idempotent(key = "#request.orderId")
// Combined parameters
@Idempotent(key = "#userId + ':' + #orderId")
// Parameter by index
@Idempotent(key = "#p0.id")idempotency:
enabled: true # Enable/disable (default true)
default-ttl: 1 # Default TTL
default-time-unit: HOURS # Default time unit
key-prefix: "idempotent:" # Redis key prefixOn duplicate request, IdempotencyException is thrown:
@ExceptionHandler(IdempotencyException.class)
public ResponseEntity<ErrorResponse> handleIdempotency(IdempotencyException ex) {
return ResponseEntity
.status(HttpStatus.CONFLICT)
.body(new ErrorResponse(ex.getMessage(), ex.getIdempotencyKey()));
}βββββββββββββββ ββββββββββββββββββββ βββββββββββββββ
β Request ββββββΆβ IdempotencyAspectββββββΆβ Redis β
βββββββββββββββ ββββββββββββββββββββ βββββββββββββββ
β β
βΌ β
βββββββββββββββββ β
β Key exists? βββββββββββββββββ
βββββββββββββββββ
β β
Yes No
β β
βΌ βΌ
βββββββββββββ βββββββββββββββββ
β Throw β β Execute methodβ
β Exception β β & save key β
βββββββββββββ βββββββββββββββββ
@Service
public class PaymentService {
@Idempotent(key = "#payment.transactionId", ttl = 24, timeUnit = TimeUnit.HOURS)
public PaymentResult processPayment(Payment payment) {
// Safe from duplicate processing
return gateway.charge(payment);
}
}@Service
public class NotificationService {
@Idempotent(key = "#userId + ':' + #eventType", ttl = 5, timeUnit = TimeUnit.MINUTES)
public void sendNotification(String userId, String eventType, String message) {
// User will receive only one notification per 5 minutes
emailService.send(userId, message);
}
}Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.