From 173a1234f99d8ed77fc7c6787ca77060eafbd27d Mon Sep 17 00:00:00 2001 From: Jason Salaber Date: Mon, 25 Nov 2024 14:37:16 -0500 Subject: [PATCH] fix: fixed retrying logic for invalid sdk key (#978) --- sdk/nodejs/__tests__/eventQueue.test.ts | 28 +++++++++++++- sdk/nodejs/src/eventQueue.ts | 49 +++++++++++++++++++++---- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/sdk/nodejs/__tests__/eventQueue.test.ts b/sdk/nodejs/__tests__/eventQueue.test.ts index 56192432d..d37022a16 100644 --- a/sdk/nodejs/__tests__/eventQueue.test.ts +++ b/sdk/nodejs/__tests__/eventQueue.test.ts @@ -13,6 +13,7 @@ import { DVCPopulatedUserFromDevCycleUser } from '../src/models/populatedUserHel import { publishEvents } from '../src/request' import testData from '@devcycle/bucketing-test-data/json-data/testData.json' +import { ResponseError } from '@devcycle/server-request' const { config } = testData const publishEvents_mock = mocked(publishEvents) @@ -231,7 +232,6 @@ describe('EventQueue Unit Tests', () => { const promise1 = eventQueue.flushEvents() const promise2 = eventQueue.flushEvents() - eventQueue.cleanup() await Promise.all([promise1, promise2]) expect(publishEvents_mock).toHaveBeenCalledTimes(1) @@ -327,6 +327,7 @@ describe('EventQueue Unit Tests', () => { ], 'localhost:3000/client/1', ) + eventQueue.cleanup() }) it( @@ -816,6 +817,31 @@ describe('EventQueue Unit Tests', () => { }, ) + it('should close the event queue if the events api response is 401', async () => { + const eventQueue = initEventQueue('sdkKey', 'uuid') + const error = new ResponseError('401 error') + error.status = 401 + publishEvents_mock.mockRejectedValueOnce(error) + eventQueue.queueEvent( + DVCPopulatedUserFromDevCycleUser({ user_id: 'user1' }), + { type: 'test_event' }, + ) + await eventQueue.flushEvents() + + expect(eventQueue.disabledEventFlush).toBe(true) + + eventQueue.queueEvent( + DVCPopulatedUserFromDevCycleUser({ user_id: 'user1' }), + { type: 'test_event' }, + ) + eventQueue.queueAggregateEvent( + DVCPopulatedUserFromDevCycleUser({ user_id: 'user1' }), + { type: EventTypes.aggVariableEvaluated, target: 'key' }, + bucketedUserConfig, + ) + await eventQueue.flushEvents() + }) + describe('Max queue size tests', () => { it( 'should not queue user event if user' + diff --git a/sdk/nodejs/src/eventQueue.ts b/sdk/nodejs/src/eventQueue.ts index ab2f2a2a3..cde7c90b2 100644 --- a/sdk/nodejs/src/eventQueue.ts +++ b/sdk/nodejs/src/eventQueue.ts @@ -49,6 +49,7 @@ export class EventQueue { eventFlushIntervalMS: number flushEventQueueSize: number maxEventQueueSize: number + disabledEventFlush: boolean private flushInterval: NodeJS.Timer private flushInProgress = false private flushCallbacks: Array<(arg: unknown) => void> = [] @@ -63,6 +64,8 @@ export class EventQueue { this.reporter = options.reporter this.eventsAPIURI = options.eventsAPIURI this.eventFlushIntervalMS = options?.eventFlushIntervalMS || 10 * 1000 + this.disabledEventFlush = false + if (this.eventFlushIntervalMS < 500) { throw new Error( `eventFlushIntervalMS: ${this.eventFlushIntervalMS} must be larger than 500ms`, @@ -121,6 +124,7 @@ export class EventQueue { cleanup(): void { clearInterval(this.flushInterval) + this.disabledEventFlush = true } private async _flushEvents() { @@ -219,12 +223,25 @@ export class EventQueue { this.logger.debug( `DevCycle Error Flushing Events response message: ${ex.message}`, ) - this.bucketing.onPayloadFailure( - this.sdkKey, - flushPayload.payloadId, - true, - ) - results.retries++ + if ('status' in ex && ex.status === 401) { + this.logger.debug( + `SDK key is invalid, closing event flushing interval`, + ) + this.bucketing.onPayloadFailure( + this.sdkKey, + flushPayload.payloadId, + false, + ) + results.failures++ + this.cleanup() + } else { + this.bucketing.onPayloadFailure( + this.sdkKey, + flushPayload.payloadId, + true, + ) + results.retries++ + } } }), ) @@ -276,9 +293,19 @@ export class EventQueue { * Queue DVCAPIEvent for publishing to DevCycle Events API. */ queueEvent(user: DVCPopulatedUser, event: DevCycleEvent): void { + if (this.disabledEventFlush) { + this.logger.warn( + `Event flushing is disabled, dropping event: ${ + event.type + }, event queue size: ${this.bucketing.eventQueueSize( + this.sdkKey, + )}`, + ) + return + } if (this.checkEventQueueSize()) { this.logger.warn( - `Max event queue size reached, dropping event: ${event}`, + `Max event queue size reached, dropping event: ${event.type}`, ) return } @@ -299,9 +326,15 @@ export class EventQueue { event: DevCycleEvent, bucketedConfig?: BucketedUserConfig, ): void { + if (this.disabledEventFlush) { + this.logger.warn( + `Event flushing is disabled, dropping aggregate event: ${event.type}`, + ) + return + } if (this.checkEventQueueSize()) { this.logger.warn( - `Max event queue size reached, dropping aggregate event: ${event}`, + `Max event queue size reached, dropping aggregate event: ${event.type}`, ) return }