Severity: Medium
Type: Bug
Scope: Stellar
Labels: bug, refactoring, Official Campaign
Description
StellarEventService.saveCursor (src/stellar/stellar-event.service.ts, line ~187) writes stellar:event_listener:cursor = cursor via cacheManager.set. The cache TTL is set globally in RedisModule (src/redis/redis.module.ts, line ~17) to 60000 ms.
If the application restarts more than 60 seconds after the last successful event, get<string>('stellar:event_listener:cursor') returns null and the listener streams from 'now' again. Between the last saved cursor and 'now', a window of unprocessed events is skipped. Restarting more frequently actually routes from the cached cursor, but events newer than 60 s after the last save are silently dropped, and there is no DLQ.
Worse: on restart with a stale cursor in the worst case, all events since the deployment are reprocessed, which can cause duplicate donation confirmations or repeated contract-event job dispatches.
Recommendation
- Persist the cursor in Postgres (a dedicated
EventCursor row) instead of Redis with TTL semantics.
- On startup, validate the cursor against the configured network and roll forward if missing.
- Add a small idempotency guard on
contract-events queue jobs (e.g. (txHash, eventType) uniqueness key) so duplicate processing is benign.
Severity: Medium
Type: Bug
Scope: Stellar
Labels:
bug,refactoring,Official CampaignDescription
StellarEventService.saveCursor(src/stellar/stellar-event.service.ts, line ~187) writesstellar:event_listener:cursor = cursorviacacheManager.set. The cache TTL is set globally inRedisModule(src/redis/redis.module.ts, line ~17) to60000ms.If the application restarts more than 60 seconds after the last successful event,
get<string>('stellar:event_listener:cursor')returnsnulland the listener streams from'now'again. Between the last saved cursor and'now', a window of unprocessed events is skipped. Restarting more frequently actually routes from the cached cursor, but events newer than 60 s after the last save are silently dropped, and there is no DLQ.Worse: on restart with a stale cursor in the worst case, all events since the deployment are reprocessed, which can cause duplicate donation confirmations or repeated contract-event job dispatches.
Recommendation
EventCursorrow) instead of Redis with TTL semantics.contract-eventsqueue jobs (e.g.(txHash, eventType)uniqueness key) so duplicate processing is benign.