Area
Notification Service / Reliability (Idempotency)
Complexity
Medium-hard
File(s)
notification-service/src/notifier.ts (notifiedRecipients, lines 19, 54-60, 160-166); notification-service/src/listener.ts (setLastLedger, line 199)
Problem
Notifier.notifiedRecipients is an in-process Set with no persistence — it exists solely to dedupe within a running process's lifetime. The cursor (processed_ledgers table) is only written after an entire batch of events has been processed. If the process crashes or restarts after some notifications were already sent for a ledger range but before setLastLedger, the next run has both a stale cursor (replaying the same events) and an empty notifiedRecipients set — so investors who already got a notification receive a duplicate. notification_history is written on every successful send but is never consulted to reconstruct dedup state on startup.
Scope
In:
- Persist dedup state (or check notification_history) so redelivered events don't re-notify investors who already have a matching history row.
Out:
- Exactly-once delivery guarantees across the webhook/email transport itself.
Acceptance Criteria
Area
Notification Service / Reliability (Idempotency)
Complexity
Medium-hard
File(s)
notification-service/src/notifier.ts (notifiedRecipients, lines 19, 54-60, 160-166); notification-service/src/listener.ts (setLastLedger, line 199)
Problem
Notifier.notifiedRecipients is an in-process Set with no persistence — it exists solely to dedupe within a running process's lifetime. The cursor (processed_ledgers table) is only written after an entire batch of events has been processed. If the process crashes or restarts after some notifications were already sent for a ledger range but before setLastLedger, the next run has both a stale cursor (replaying the same events) and an empty notifiedRecipients set — so investors who already got a notification receive a duplicate. notification_history is written on every successful send but is never consulted to reconstruct dedup state on startup.
Scope
In:
Out:
Acceptance Criteria