@@ -18,13 +18,13 @@ export type DynamicFlushSchedulerConfig<T> = {
1818} ;
1919
2020export class DynamicFlushScheduler < T > {
21- private batchQueue : T [ ] [ ] ;
22- private currentBatch : T [ ] ;
21+ private batchQueue : T [ ] [ ] ;
22+ private currentBatch : T [ ] ;
2323 private readonly BATCH_SIZE : number ;
2424 private readonly FLUSH_INTERVAL : number ;
2525 private flushTimer : NodeJS . Timeout | null ;
2626 private readonly callback : ( flushId : string , batch : T [ ] ) => Promise < void > ;
27-
27+
2828 // New properties for dynamic scaling
2929 private readonly minConcurrency : number ;
3030 private readonly maxConcurrency : number ;
@@ -57,36 +57,36 @@ export class DynamicFlushScheduler<T> {
5757 this . FLUSH_INTERVAL = config . flushInterval ;
5858 this . callback = config . callback ;
5959 this . flushTimer = null ;
60-
60+
6161 // Initialize dynamic scaling parameters
6262 this . minConcurrency = config . minConcurrency ?? 1 ;
6363 this . maxConcurrency = config . maxConcurrency ?? 10 ;
6464 this . maxBatchSize = config . maxBatchSize ?? config . batchSize * 5 ;
6565 this . memoryPressureThreshold = config . memoryPressureThreshold ?? config . batchSize * 20 ;
66-
66+
6767 // Initialize load shedding parameters
6868 this . loadSheddingThreshold = config . loadSheddingThreshold ?? config . batchSize * 50 ;
6969 this . loadSheddingEnabled = config . loadSheddingEnabled ?? true ;
7070 this . isDroppableEvent = config . isDroppableEvent ;
71-
71+
7272 // Start with minimum concurrency
7373 this . limiter = pLimit ( this . minConcurrency ) ;
74-
74+
7575 this . startFlushTimer ( ) ;
7676 this . startMetricsReporter ( ) ;
7777 }
7878
7979 addToBatch ( items : T [ ] ) : void {
8080 let itemsToAdd = items ;
81-
81+
8282 // Apply load shedding if enabled and we're over the threshold
8383 if ( this . loadSheddingEnabled && this . totalQueuedItems >= this . loadSheddingThreshold ) {
8484 const { kept, dropped } = this . applyLoadShedding ( items ) ;
8585 itemsToAdd = kept ;
86-
86+
8787 if ( dropped . length > 0 ) {
8888 this . metrics . droppedEvents += dropped . length ;
89-
89+
9090 // Track dropped events by kind if possible
9191 dropped . forEach ( ( item ) => {
9292 const kind = this . getEventKind ( item ) ;
@@ -95,7 +95,7 @@ export class DynamicFlushScheduler<T> {
9595 this . metrics . droppedEventsByKind . set ( kind , currentCount + 1 ) ;
9696 }
9797 } ) ;
98-
98+
9999 if ( ! this . isLoadShedding ) {
100100 this . isLoadShedding = true ;
101101 logger . warn ( "Load shedding activated" , {
@@ -113,22 +113,22 @@ export class DynamicFlushScheduler<T> {
113113 totalDropped : this . metrics . droppedEvents ,
114114 } ) ;
115115 }
116-
116+
117117 this . currentBatch . push ( ...itemsToAdd ) ;
118118 this . totalQueuedItems += itemsToAdd . length ;
119119
120120 // Check if we need to create a batch
121121 if ( this . currentBatch . length >= this . currentBatchSize ) {
122122 this . createBatch ( ) ;
123123 }
124-
124+
125125 // Adjust concurrency based on queue pressure
126126 this . adjustConcurrency ( ) ;
127127 }
128128
129129 private createBatch ( ) : void {
130130 if ( this . currentBatch . length === 0 ) return ;
131-
131+
132132 this . batchQueue . push ( this . currentBatch ) ;
133133 this . currentBatch = [ ] ;
134134 this . flushBatches ( ) ;
@@ -155,34 +155,34 @@ export class DynamicFlushScheduler<T> {
155155
156156 private async flushBatches ( ) : Promise < void > {
157157 const batchesToFlush : T [ ] [ ] = [ ] ;
158-
158+
159159 // Dequeue all available batches up to current concurrency limit
160160 while ( this . batchQueue . length > 0 && batchesToFlush . length < this . limiter . concurrency ) {
161161 const batch = this . batchQueue . shift ( ) ;
162162 if ( batch ) {
163163 batchesToFlush . push ( batch ) ;
164164 }
165165 }
166-
166+
167167 if ( batchesToFlush . length === 0 ) return ;
168168
169169 // Schedule all batches for concurrent processing
170170 const flushPromises = batchesToFlush . map ( ( batch ) =>
171171 this . limiter ( async ( ) => {
172172 const flushId = nanoid ( ) ;
173173 const itemCount = batch . length ;
174-
174+
175175 try {
176176 const startTime = Date . now ( ) ;
177177 await this . callback ( flushId , batch ) ;
178-
178+
179179 const duration = Date . now ( ) - startTime ;
180180 this . totalQueuedItems -= itemCount ;
181181 this . consecutiveFlushFailures = 0 ;
182182 this . lastFlushTime = Date . now ( ) ;
183183 this . metrics . flushedBatches ++ ;
184184 this . metrics . totalItemsFlushed += itemCount ;
185-
185+
186186 logger . debug ( "Batch flushed successfully" , {
187187 flushId,
188188 itemCount,
@@ -194,26 +194,26 @@ export class DynamicFlushScheduler<T> {
194194 } catch ( error ) {
195195 this . consecutiveFlushFailures ++ ;
196196 this . metrics . failedBatches ++ ;
197-
197+
198198 logger . error ( "Error flushing batch" , {
199199 flushId,
200200 itemCount,
201201 error,
202202 consecutiveFailures : this . consecutiveFlushFailures ,
203203 } ) ;
204-
204+
205205 // Re-queue the batch at the front if it fails
206206 this . batchQueue . unshift ( batch ) ;
207207 this . totalQueuedItems += itemCount ;
208-
208+
209209 // Back off on failures
210210 if ( this . consecutiveFlushFailures > 3 ) {
211211 this . adjustConcurrency ( true ) ;
212212 }
213213 }
214214 } )
215215 ) ;
216-
216+
217217 // Don't await here - let them run concurrently
218218 Promise . allSettled ( flushPromises ) . then ( ( ) => {
219219 // After flush completes, check if we need to flush more
@@ -226,15 +226,15 @@ export class DynamicFlushScheduler<T> {
226226 private adjustConcurrency ( backOff : boolean = false ) : void {
227227 const currentConcurrency = this . limiter . concurrency ;
228228 let newConcurrency = currentConcurrency ;
229-
229+
230230 if ( backOff ) {
231231 // Reduce concurrency on failures
232232 newConcurrency = Math . max ( this . minConcurrency , Math . floor ( currentConcurrency * 0.75 ) ) ;
233233 } else {
234234 // Calculate pressure metrics
235235 const queuePressure = this . totalQueuedItems / this . memoryPressureThreshold ;
236236 const timeSinceLastFlush = Date . now ( ) - this . lastFlushTime ;
237-
237+
238238 if ( queuePressure > 0.8 || timeSinceLastFlush > this . FLUSH_INTERVAL * 2 ) {
239239 // High pressure - increase concurrency
240240 newConcurrency = Math . min ( this . maxConcurrency , currentConcurrency + 2 ) ;
@@ -243,7 +243,7 @@ export class DynamicFlushScheduler<T> {
243243 newConcurrency = Math . max ( this . minConcurrency , currentConcurrency - 1 ) ;
244244 }
245245 }
246-
246+
247247 // Adjust batch size based on pressure
248248 if ( this . totalQueuedItems > this . memoryPressureThreshold ) {
249249 this . currentBatchSize = Math . min (
@@ -253,11 +253,11 @@ export class DynamicFlushScheduler<T> {
253253 } else {
254254 this . currentBatchSize = this . BATCH_SIZE ;
255255 }
256-
256+
257257 // Update concurrency if changed
258258 if ( newConcurrency !== currentConcurrency ) {
259259 this . limiter = pLimit ( newConcurrency ) ;
260-
260+
261261 logger . info ( "Adjusted flush concurrency" , {
262262 previousConcurrency : currentConcurrency ,
263263 newConcurrency,
@@ -267,15 +267,15 @@ export class DynamicFlushScheduler<T> {
267267 } ) ;
268268 }
269269 }
270-
270+
271271 private startMetricsReporter ( ) : void {
272272 // Report metrics every 30 seconds
273273 setInterval ( ( ) => {
274274 const droppedByKind : Record < string , number > = { } ;
275275 this . metrics . droppedEventsByKind . forEach ( ( count , kind ) => {
276276 droppedByKind [ kind ] = count ;
277277 } ) ;
278-
278+
279279 logger . info ( "DynamicFlushScheduler metrics" , {
280280 totalQueuedItems : this . totalQueuedItems ,
281281 batchQueueLength : this . batchQueue . length ,
@@ -287,7 +287,7 @@ export class DynamicFlushScheduler<T> {
287287 isLoadShedding : this . isLoadShedding ,
288288 metrics : {
289289 ...this . metrics ,
290- droppedEventsByKind ,
290+ droppedByKind ,
291291 } ,
292292 } ) ;
293293 } , 30000 ) ;
@@ -298,24 +298,24 @@ export class DynamicFlushScheduler<T> {
298298 // If no function provided to determine droppable events, keep all
299299 return { kept : items , dropped : [ ] } ;
300300 }
301-
301+
302302 const kept : T [ ] = [ ] ;
303303 const dropped : T [ ] = [ ] ;
304-
304+
305305 for ( const item of items ) {
306306 if ( this . isDroppableEvent ( item ) ) {
307307 dropped . push ( item ) ;
308308 } else {
309309 kept . push ( item ) ;
310310 }
311311 }
312-
312+
313313 return { kept, dropped } ;
314314 }
315-
315+
316316 private getEventKind ( item : T ) : string | undefined {
317317 // Try to extract the kind from the event if it has one
318- if ( item && typeof item === ' object' && ' kind' in item ) {
318+ if ( item && typeof item === " object" && " kind" in item ) {
319319 return String ( item . kind ) ;
320320 }
321321 return undefined ;
@@ -327,7 +327,7 @@ export class DynamicFlushScheduler<T> {
327327 this . metrics . droppedEventsByKind . forEach ( ( count , kind ) => {
328328 droppedByKind [ kind ] = count ;
329329 } ) ;
330-
330+
331331 return {
332332 queuedItems : this . totalQueuedItems ,
333333 batchQueueLength : this . batchQueue . length ,
@@ -336,7 +336,7 @@ export class DynamicFlushScheduler<T> {
336336 activeFlushes : this . limiter . activeCount ,
337337 pendingFlushes : this . limiter . pendingCount ,
338338 isLoadShedding : this . isLoadShedding ,
339- metrics : {
339+ metrics : {
340340 ...this . metrics ,
341341 droppedEventsByKind : droppedByKind ,
342342 } ,
@@ -348,12 +348,12 @@ export class DynamicFlushScheduler<T> {
348348 if ( this . flushTimer ) {
349349 clearInterval ( this . flushTimer ) ;
350350 }
351-
351+
352352 // Flush any remaining items
353353 if ( this . currentBatch . length > 0 ) {
354354 this . createBatch ( ) ;
355355 }
356-
356+
357357 // Wait for all pending flushes to complete
358358 while ( this . batchQueue . length > 0 || this . limiter . activeCount > 0 ) {
359359 await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
0 commit comments