From adc53159483f48ebdbe8a31fc06b2ebede1b003c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=97=D0=B8=D0=BC=D0=B8=D0=BD?= Date: Mon, 4 May 2026 00:50:14 +0300 Subject: [PATCH] =?UTF-8?q?feat(idempotency):=20scheduled=20cleanup=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20expired=20keys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @Scheduled(cron='0 17 * * * *') запускает deleteExpired раз в час (17-я минута, чтобы не совпало с другими cron). Конфигурируется через ordinis.idempotency.cleanup-cron. Counter ordinis_idempotency_cleaned_total для observability. --- .../idempotency/IdempotencyCleanupJob.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/idempotency/IdempotencyCleanupJob.java 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()); + } + } +}