@@ -5,12 +5,19 @@ import { getEventName } from '../utils/event-utils';
55import { getNotificationAnalyticsAggregator , NotificationAnalyticsAggregator } from './notification-analytics-aggregator' ;
66import { NotificationType } from '../types/scheduled-notification' ;
77
8+ export enum Priority {
9+ Low = 0 ,
10+ Medium = 1 ,
11+ High = 2 ,
12+ }
13+
814export interface RetryQueueOptions {
915 baseDelayMs ?: number ;
1016 multiplier ?: number ;
1117 jitter ?: boolean ;
1218 maxRetries ?: number ;
1319 processIntervalMs ?: number ;
20+ priorityWeights ?: { high : number ; medium : number ; low : number } ;
1421}
1522
1623interface RetryItem {
@@ -19,6 +26,8 @@ interface RetryItem {
1926 retryCount : number ;
2027 nextRetryAt : number ;
2128 requestId ?: string ;
29+ priority : Priority ;
30+ enqueuedAt : number ;
2231}
2332
2433const DEFAULTS = {
@@ -27,6 +36,7 @@ const DEFAULTS = {
2736 jitter : true ,
2837 maxRetries : 5 ,
2938 processIntervalMs : 5_000 ,
39+ priorityWeights : { high : 5 , medium : 2 , low : 1 } ,
3040} ;
3141
3242export type NotificationFn = (
@@ -43,9 +53,11 @@ export class NotificationRetryQueue {
4353 private readonly jitter : boolean ;
4454 private readonly maxRetries : number ;
4555 private readonly processIntervalMs : number ;
56+ private readonly priorityWeights : { high : number ; medium : number ; low : number } ;
4657 private timer : ReturnType < typeof setInterval > | null = null ;
4758 private readonly notificationFn : NotificationFn ;
4859 private readonly analytics : NotificationAnalyticsAggregator | null ;
60+ private priorityCounters : { high : number ; medium : number ; low : number } = { high : 0 , medium : 0 , low : 0 } ;
4961
5062 constructor ( notificationFn : NotificationFn , options ?: RetryQueueOptions ) {
5163 this . notificationFn = notificationFn ;
@@ -54,13 +66,15 @@ export class NotificationRetryQueue {
5466 this . jitter = options ?. jitter ?? DEFAULTS . jitter ;
5567 this . maxRetries = options ?. maxRetries ?? DEFAULTS . maxRetries ;
5668 this . processIntervalMs = options ?. processIntervalMs ?? DEFAULTS . processIntervalMs ;
69+ this . priorityWeights = options ?. priorityWeights ?? DEFAULTS . priorityWeights ;
5770 this . analytics = getNotificationAnalyticsAggregator ( ) ;
5871 }
5972
6073 enqueue (
6174 event : StellarSDK . rpc . Api . EventResponse ,
6275 contractConfig : ContractConfig ,
63- requestId ?: string
76+ requestId ?: string ,
77+ priority : Priority = Priority . Medium
6478 ) : void {
6579 const fingerprint = buildRetryFingerprint ( event , contractConfig . address ) ;
6680
@@ -84,10 +98,11 @@ export class NotificationRetryQueue {
8498 delayMs,
8599 nextRetryAt : new Date ( nextRetryAt ) . toISOString ( ) ,
86100 maxRetries : this . maxRetries ,
101+ priority : Priority [ priority ] ,
87102 } ) ;
88103
89104 this . queuedFingerprints . add ( fingerprint ) ;
90- this . queue . push ( { event, contractConfig, retryCount : 0 , nextRetryAt, requestId } ) ;
105+ this . queue . push ( { event, contractConfig, retryCount : 0 , nextRetryAt, requestId, priority , enqueuedAt : Date . now ( ) } ) ;
91106 }
92107
93108 start ( ) : void {
@@ -112,14 +127,41 @@ export class NotificationRetryQueue {
112127
113128 private async processQueue ( ) : Promise < void > {
114129 const now = Date . now ( ) ;
115- const due = this . queue . filter ( ( item ) => item . nextRetryAt <= now ) ;
130+ const due = this . queue
131+ . filter ( ( item ) => item . nextRetryAt <= now )
132+ . sort ( ( a , b ) => {
133+ const priorityA = this . getWeightedPriority ( a ) ;
134+ const priorityB = this . getWeightedPriority ( b ) ;
135+ if ( priorityB !== priorityA ) return priorityB - priorityA ;
136+ return a . enqueuedAt - b . enqueuedAt ;
137+ } ) ;
138+
116139 this . queue = this . queue . filter ( ( item ) => item . nextRetryAt > now ) ;
117140
141+ for ( const item of due ) {
142+ if ( item . priority === Priority . High ) this . priorityCounters . high ++ ;
143+ else if ( item . priority === Priority . Medium ) this . priorityCounters . medium ++ ;
144+ else this . priorityCounters . low ++ ;
145+ }
146+
118147 for ( const item of due ) {
119148 await this . retryItem ( item ) ;
120149 }
121150 }
122151
152+ private getWeightedPriority ( item : RetryItem ) : number {
153+ const basePriority = item . priority ;
154+ const age = Date . now ( ) - item . enqueuedAt ;
155+ const ageBonus = Math . floor ( age / 60000 ) ;
156+
157+ let weight = 0 ;
158+ if ( item . priority === Priority . High ) weight = this . priorityWeights . high ;
159+ else if ( item . priority === Priority . Medium ) weight = this . priorityWeights . medium ;
160+ else weight = this . priorityWeights . low ;
161+
162+ return basePriority + ageBonus + weight ;
163+ }
164+
123165 private async retryItem ( item : RetryItem ) : Promise < void > {
124166 const attempt = item . retryCount + 1 ;
125167 const fingerprint = buildRetryFingerprint ( item . event , item . contractConfig . address ) ;
0 commit comments