diff --git a/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/idempotency/IdempotencyCleanupJob.java b/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/idempotency/IdempotencyCleanupJob.java new file mode 100644 index 0000000..fbefde3 --- /dev/null +++ b/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/idempotency/IdempotencyCleanupJob.java @@ -0,0 +1,46 @@ +package cloud.nstart.terravault.ordinis.restapi.idempotency; + +import cloud.nstart.terravault.ordinis.domain.idempotency.IdempotencyKeyRepository; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.OffsetDateTime; + +/** + * Scheduled cleanup для idempotency_keys. + * + *
Retention 30 дней (см. migration 0006). Запускается раз в час, удаляет + * записи с expires_at < now. Неблокирующий — простой DELETE по индексу. + */ +@Component +public class IdempotencyCleanupJob { + + private static final Logger log = LoggerFactory.getLogger(IdempotencyCleanupJob.class); + + private final IdempotencyKeyRepository repository; + private final Counter cleanedCounter; + + public IdempotencyCleanupJob(IdempotencyKeyRepository repository, MeterRegistry meterRegistry) { + this.repository = repository; + this.cleanedCounter = Counter.builder("ordinis_idempotency_cleaned_total") + .description("Удалённые expired idempotency keys") + .register(meterRegistry); + } + + @Scheduled(cron = "${ordinis.idempotency.cleanup-cron:0 17 * * * *}") + public void cleanup() { + try { + int deleted = repository.deleteExpired(OffsetDateTime.now()); + if (deleted > 0) { + log.info("Idempotency cleanup: удалено {} expired ключей", deleted); + cleanedCounter.increment(deleted); + } + } catch (Exception e) { + log.warn("Idempotency cleanup failed: {}", e.getMessage()); + } + } +}