fix(notifications): dispatcher race с OutboxPoller — emails never sent
This commit is contained in:
+12
-6
@@ -148,16 +148,22 @@ public class NotificationsDispatcher {
|
||||
/**
|
||||
* Scan recent outbox events for draft lifecycle types and process each.
|
||||
* Runs every {@code ordinis.notifications.poll-interval-ms} ms.
|
||||
*
|
||||
* <p>Uses {@link OutboxEventRepository#findRecentByEventTypes} (event_type
|
||||
* + createdAt window, NOT published_at). Раньше использовался
|
||||
* {@code findUnpublished} что race'илось с OutboxPoller (1s tick) — Poller
|
||||
* setting {@code published_at} быстрее чем notifications 30s tick → events
|
||||
* никогда не reached dispatcher. Now: dispatcher polls all recent draft
|
||||
* events независимо от Kafka publish state, idempotency через
|
||||
* notification_log UNIQUE (event_id, channel, recipient).
|
||||
*/
|
||||
@Scheduled(fixedDelayString = "${ordinis.notifications.poll-interval-ms:30000}")
|
||||
@Transactional
|
||||
public void sendTick() {
|
||||
List<OutboxEvent> events = outboxRepository.findUnpublished(PageRequest.of(0, batchSize));
|
||||
if (events.isEmpty()) return;
|
||||
|
||||
List<OutboxEvent> drafts = events.stream()
|
||||
.filter(e -> DRAFT_EVENT_TYPES.contains(e.getEventType()))
|
||||
.toList();
|
||||
// 24h window — covers max retention scenarios без full table scan.
|
||||
OffsetDateTime since = OffsetDateTime.now().minusHours(24);
|
||||
List<OutboxEvent> drafts = outboxRepository.findRecentByEventTypes(
|
||||
DRAFT_EVENT_TYPES, since, PageRequest.of(0, batchSize));
|
||||
|
||||
if (drafts.isEmpty()) return;
|
||||
log.debug("NotificationsDispatcher sendTick: {} draft events to process", drafts.size());
|
||||
|
||||
Reference in New Issue
Block a user