@@ -18,10 +18,10 @@ import { EventProcessor, ProcessableEvent } from "./event_processor";
18
18
import { getBatchedAsync , getBatchedSync , Store } from "../utils/cache/store" ;
19
19
import { EventDispatcher , EventDispatcherResponse , LogEvent } from "./event_dispatcher/event_dispatcher" ;
20
20
import { buildLogEvent } from "./event_builder/log_event" ;
21
- import { BackoffController , ExponentialBackoff , IntervalRepeater , Repeater } from "../utils/repeater/repeater" ;
21
+ import { BackoffController , ExponentialBackoff , Repeater } from "../utils/repeater/repeater" ;
22
22
import { LoggerFacade } from '../logging/logger' ;
23
23
import { BaseService , ServiceState , StartupLog } from "../service" ;
24
- import { Consumer , Fn , Producer } from "../utils/type" ;
24
+ import { Consumer , Fn , Maybe , Producer } from "../utils/type" ;
25
25
import { RunResult , runWithRetry } from "../utils/executor/backoff_retry_runner" ;
26
26
import { isSuccessStatusCode } from "../utils/http_request_handler/http_util" ;
27
27
import { EventEmitter } from "../utils/event_emitter/event_emitter" ;
@@ -31,13 +31,16 @@ import { FAILED_TO_DISPATCH_EVENTS, SERVICE_NOT_RUNNING } from "error_message";
31
31
import { OptimizelyError } from "../error/optimizly_error" ;
32
32
import { sprintf } from "../utils/fns" ;
33
33
import { SERVICE_STOPPED_BEFORE_RUNNING } from "../service" ;
34
+ import { EVENT_STORE_FULL } from "../message/log_message" ;
34
35
35
36
export const DEFAULT_MIN_BACKOFF = 1000 ;
36
37
export const DEFAULT_MAX_BACKOFF = 32000 ;
38
+ export const MAX_EVENTS_IN_STORE = 500 ;
37
39
38
40
export type EventWithId = {
39
41
id : string ;
40
42
event : ProcessableEvent ;
43
+ notStored ?: boolean ;
41
44
} ;
42
45
43
46
export type RetryConfig = {
@@ -59,7 +62,7 @@ export type BatchEventProcessorConfig = {
59
62
60
63
type EventBatch = {
61
64
request : LogEvent ,
62
- ids : string [ ] ,
65
+ events : EventWithId [ ] ,
63
66
}
64
67
65
68
export const LOGGER_NAME = 'BatchEventProcessor' ;
@@ -70,11 +73,13 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
70
73
private eventQueue : EventWithId [ ] = [ ] ;
71
74
private batchSize : number ;
72
75
private eventStore ?: Store < EventWithId > ;
76
+ private eventCountInStore : Maybe < number > = undefined ;
77
+ private maxEventsInStore : number = MAX_EVENTS_IN_STORE ;
73
78
private dispatchRepeater : Repeater ;
74
79
private failedEventRepeater ?: Repeater ;
75
80
private idGenerator : IdGenerator = new IdGenerator ( ) ;
76
81
private runningTask : Map < string , RunResult < EventDispatcherResponse > > = new Map ( ) ;
77
- private dispatchingEventIds : Set < string > = new Set ( ) ;
82
+ private dispatchingEvents : Map < string , EventWithId > = new Map ( ) ;
78
83
private eventEmitter : EventEmitter < { dispatch : LogEvent } > = new EventEmitter ( ) ;
79
84
private retryConfig ?: RetryConfig ;
80
85
@@ -84,11 +89,13 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
84
89
this . closingEventDispatcher = config . closingEventDispatcher ;
85
90
this . batchSize = config . batchSize ;
86
91
this . eventStore = config . eventStore ;
92
+
87
93
this . retryConfig = config . retryConfig ;
88
94
89
95
this . dispatchRepeater = config . dispatchRepeater ;
90
96
this . dispatchRepeater . setTask ( ( ) => this . flush ( ) ) ;
91
97
98
+ this . maxEventsInStore = Math . max ( 2 * config . batchSize , MAX_EVENTS_IN_STORE ) ;
92
99
this . failedEventRepeater = config . failedEventRepeater ;
93
100
this . failedEventRepeater ?. setTask ( ( ) => this . retryFailedEvents ( ) ) ;
94
101
if ( config . logger ) {
@@ -111,7 +118,7 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
111
118
}
112
119
113
120
const keys = ( await this . eventStore . getKeys ( ) ) . filter (
114
- ( k ) => ! this . dispatchingEventIds . has ( k ) && ! this . eventQueue . find ( ( e ) => e . id === k )
121
+ ( k ) => ! this . dispatchingEvents . has ( k ) && ! this . eventQueue . find ( ( e ) => e . id === k )
115
122
) ;
116
123
117
124
const events = await ( this . eventStore . operation === 'sync' ?
@@ -138,7 +145,7 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
138
145
( currentBatch . length > 0 && ! areEventContextsEqual ( currentBatch [ 0 ] . event , event . event ) ) ) {
139
146
batches . push ( {
140
147
request : buildLogEvent ( currentBatch . map ( ( e ) => e . event ) ) ,
141
- ids : currentBatch . map ( ( e ) => e . id ) ,
148
+ events : currentBatch ,
142
149
} ) ;
143
150
currentBatch = [ ] ;
144
151
}
@@ -148,7 +155,7 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
148
155
if ( currentBatch . length > 0 ) {
149
156
batches . push ( {
150
157
request : buildLogEvent ( currentBatch . map ( ( e ) => e . event ) ) ,
151
- ids : currentBatch . map ( ( e ) => e . id ) ,
158
+ events : currentBatch ,
152
159
} ) ;
153
160
}
154
161
@@ -163,15 +170,15 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
163
170
}
164
171
165
172
const events : ProcessableEvent [ ] = [ ] ;
166
- const ids : string [ ] = [ ] ;
173
+ const eventWithIds : EventWithId [ ] = [ ] ;
167
174
168
175
this . eventQueue . forEach ( ( event ) => {
169
176
events . push ( event . event ) ;
170
- ids . push ( event . id ) ;
177
+ eventWithIds . push ( event ) ;
171
178
} ) ;
172
179
173
180
this . eventQueue = [ ] ;
174
- return { request : buildLogEvent ( events ) , ids } ;
181
+ return { request : buildLogEvent ( events ) , events : eventWithIds } ;
175
182
}
176
183
177
184
private async executeDispatch ( request : LogEvent , closing = false ) : Promise < EventDispatcherResponse > {
@@ -185,10 +192,10 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
185
192
}
186
193
187
194
private dispatchBatch ( batch : EventBatch , closing : boolean ) : void {
188
- const { request, ids } = batch ;
195
+ const { request, events } = batch ;
189
196
190
- ids . forEach ( ( id ) => {
191
- this . dispatchingEventIds . add ( id ) ;
197
+ events . forEach ( ( event ) => {
198
+ this . dispatchingEvents . set ( event . id , event ) ;
192
199
} ) ;
193
200
194
201
const runResult : RunResult < EventDispatcherResponse > = this . retryConfig
@@ -205,9 +212,11 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
205
212
this . runningTask . set ( taskId , runResult ) ;
206
213
207
214
runResult . result . then ( ( res ) => {
208
- ids . forEach ( ( id ) => {
209
- this . dispatchingEventIds . delete ( id ) ;
210
- this . eventStore ?. remove ( id ) ;
215
+ events . forEach ( ( event ) => {
216
+ this . eventStore ?. remove ( event . id ) ;
217
+ if ( ! event . notStored && this . eventCountInStore ) {
218
+ this . eventCountInStore -- ;
219
+ }
211
220
} ) ;
212
221
return Promise . resolve ( ) ;
213
222
} ) . catch ( ( err ) => {
@@ -216,7 +225,7 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
216
225
this . logger ?. error ( err ) ;
217
226
} ) . finally ( ( ) => {
218
227
this . runningTask . delete ( taskId ) ;
219
- ids . forEach ( ( id ) => this . dispatchingEventIds . delete ( id ) ) ;
228
+ events . forEach ( ( event ) => this . dispatchingEvents . delete ( event . id ) ) ;
220
229
} ) ;
221
230
}
222
231
@@ -235,12 +244,12 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
235
244
return Promise . reject ( new OptimizelyError ( SERVICE_NOT_RUNNING , 'BatchEventProcessor' ) ) ;
236
245
}
237
246
238
- const eventWithId = {
247
+ const eventWithId : EventWithId = {
239
248
id : this . idGenerator . getId ( ) ,
240
249
event : event ,
241
250
} ;
242
251
243
- await this . eventStore ?. set ( eventWithId . id , eventWithId ) ;
252
+ await this . storeEvent ( eventWithId ) ;
244
253
245
254
if ( this . eventQueue . length > 0 && ! areEventContextsEqual ( this . eventQueue [ 0 ] . event , event ) ) {
246
255
this . flush ( ) ;
@@ -253,7 +262,35 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
253
262
} else if ( ! this . dispatchRepeater . isRunning ( ) ) {
254
263
this . dispatchRepeater . start ( ) ;
255
264
}
265
+ }
266
+
267
+ private async findEventCountInStore ( ) : Promise < void > {
268
+ if ( this . eventStore && this . eventCountInStore === undefined ) {
269
+ try {
270
+ const keys = await this . eventStore . getKeys ( ) ;
271
+ this . eventCountInStore = keys . length ;
272
+ } catch ( e ) {
273
+ this . logger ?. error ( e ) ;
274
+ }
275
+ }
276
+ }
256
277
278
+ private async storeEvent ( eventWithId : EventWithId ) : Promise < void > {
279
+ await this . findEventCountInStore ( ) ;
280
+ if ( this . eventCountInStore !== undefined && this . eventCountInStore >= this . maxEventsInStore ) {
281
+ this . logger ?. info ( EVENT_STORE_FULL , eventWithId . event . uuid ) ;
282
+ eventWithId . notStored = true ;
283
+ return ;
284
+ }
285
+
286
+ await Promise . resolve ( this . eventStore ?. set ( eventWithId . id , eventWithId ) ) . then ( ( ) => {
287
+ if ( this . eventCountInStore !== undefined ) {
288
+ this . eventCountInStore ++ ;
289
+ }
290
+ } ) . catch ( ( e ) => {
291
+ eventWithId . notStored = true ;
292
+ this . logger ?. error ( e ) ;
293
+ } ) ;
257
294
}
258
295
259
296
start ( ) : void {
0 commit comments