diff --git a/CHANGES.txt b/CHANGES.txt index 00388146..3d681b60 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,6 @@ +1.4.1 (June 13, 2022) + - Bugfixing - Updated submitters logic, to avoid dropping impressions and events that are being tracked while POST request is pending. + 1.4.0 (May 24, 2022) - Added `scheduler.telemetryRefreshRate` property to SDK configuration, and deprecated `scheduler.metricsRefreshRate` property. - Updated SDK telemetry storage, metrics and updater to be more effective and send less often. diff --git a/package-lock.json b/package-lock.json index 2be921ba..5f3b792a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.4.0", + "version": "1.4.1-rc.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4836,19 +4836,19 @@ "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", "dev": true }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", "dev": true }, "lodash.isequal": { diff --git a/package.json b/package.json index 671e18f6..d7f18b67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.4.0", + "version": "1.4.1-rc.2", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/listeners/__tests__/browser.spec.ts b/src/listeners/__tests__/browser.spec.ts index 4657d24e..566d6a68 100644 --- a/src/listeners/__tests__/browser.spec.ts +++ b/src/listeners/__tests__/browser.spec.ts @@ -9,7 +9,7 @@ jest.mock('../../sync/submitters/telemetrySubmitter', () => { return { isEmpty: () => false, clear: () => { }, - state: () => ({}), + pop: () => ({}), }; } }; @@ -43,21 +43,21 @@ const fakeStorageOptimized = { // @ts-expect-error impressions: { isEmpty: jest.fn(), clear: jest.fn(), - state() { + pop() { return [fakeImpression]; } } as IImpressionsCacheSync, // @ts-expect-error events: { isEmpty: jest.fn(), clear: jest.fn(), - state() { + pop() { return [fakeEvent]; } } as IEventsCacheSync, // @ts-expect-error impressionCounts: { isEmpty: jest.fn(), clear: jest.fn(), - state() { + pop() { return fakeImpressionCounts; } } as IImpressionCountsCacheSync, diff --git a/src/listeners/browser.ts b/src/listeners/browser.ts index faf5c956..77574eba 100644 --- a/src/listeners/browser.ts +++ b/src/listeners/browser.ts @@ -89,10 +89,10 @@ export class BrowserSignalListener implements ISignalListener { if (this.syncManager.pushManager) this.syncManager.pushManager.stop(); } - private _flushData(url: string, cache: IRecorderCacheProducerSync, postService: (body: string) => Promise, fromCacheToPayload?: (cacheData: TState) => any, extraMetadata?: {}) { + private _flushData(url: string, cache: IRecorderCacheProducerSync, postService: (body: string) => Promise, fromCacheToPayload?: (cacheData: T) => any, extraMetadata?: {}) { // if there is data in cache, send it to backend if (!cache.isEmpty()) { - const dataPayload = fromCacheToPayload ? fromCacheToPayload(cache.state()) : cache.state(); + const dataPayload = fromCacheToPayload ? fromCacheToPayload(cache.pop()) : cache.pop(); if (!this._sendBeacon(url, dataPayload, extraMetadata)) { postService(JSON.stringify(dataPayload)).catch(() => { }); // no-op just to catch a possible exception } diff --git a/src/storages/__tests__/testUtils.ts b/src/storages/__tests__/testUtils.ts index 520dba1d..e2724a3f 100644 --- a/src/storages/__tests__/testUtils.ts +++ b/src/storages/__tests__/testUtils.ts @@ -14,7 +14,7 @@ export function assertStorageInterface(storage: IStorageSync | IStorageAsync) { export function assertSyncRecorderCacheInterface(cache: IEventsCacheSync | IImpressionsCacheSync) { expect(typeof cache.isEmpty).toBe('function'); expect(typeof cache.clear).toBe('function'); - expect(typeof cache.state).toBe('function'); + expect(typeof cache.pop).toBe('function'); expect(typeof cache.track).toBe('function'); } diff --git a/src/storages/inMemory/EventsCacheInMemory.ts b/src/storages/inMemory/EventsCacheInMemory.ts index d209cba6..64525cdf 100644 --- a/src/storages/inMemory/EventsCacheInMemory.ts +++ b/src/storages/inMemory/EventsCacheInMemory.ts @@ -46,10 +46,12 @@ export class EventsCacheInMemory implements IEventsCacheSync { } /** - * Get the collected data, used as payload for posting. + * Pop the collected data, used as payload for posting. */ - state() { - return this.queue; + pop(toMerge?: SplitIO.EventData[]) { + const data = this.queue; + this.clear(); + return toMerge ? toMerge.concat(data) : data; } /** diff --git a/src/storages/inMemory/ImpressionCountsCacheInMemory.ts b/src/storages/inMemory/ImpressionCountsCacheInMemory.ts index f7598d2a..de32ab19 100644 --- a/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +++ b/src/storages/inMemory/ImpressionCountsCacheInMemory.ts @@ -20,17 +20,34 @@ export class ImpressionCountsCacheInMemory implements IImpressionCountsCacheSync this.cache[key] = currentAmount ? currentAmount + amount : amount; } + + /** - * Returns all the elements stored in the cache and resets the cache. - */ - state() { - return this.cache; + * Pop the collected data, used as payload for posting. + */ + pop(toMerge?: Record) { + const data = this.cache; + this.clear(); + if (toMerge) { + Object.keys(data).forEach((key) => { + if (toMerge[key]) toMerge[key] += data[key]; + else toMerge[key] = data[key]; + }); + return toMerge; + } + return data; } + /** + * Clear the data stored on the cache. + */ clear() { this.cache = {}; } + /** + * Check if the cache is empty. + */ isEmpty() { return Object.keys(this.cache).length === 0; } diff --git a/src/storages/inMemory/ImpressionsCacheInMemory.ts b/src/storages/inMemory/ImpressionsCacheInMemory.ts index 57621c71..a3d46634 100644 --- a/src/storages/inMemory/ImpressionsCacheInMemory.ts +++ b/src/storages/inMemory/ImpressionsCacheInMemory.ts @@ -41,10 +41,12 @@ export class ImpressionsCacheInMemory implements IImpressionsCacheSync { } /** - * Get the collected data, used as payload for posting. + * Pop the collected data, used as payload for posting. */ - state() { - return this.queue; + pop(toMerge?: ImpressionDTO[]) { + const data = this.queue; + this.clear(); + return toMerge ? toMerge.concat(data) : data; } /** diff --git a/src/storages/inMemory/__tests__/EventsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/EventsCacheInMemory.spec.ts index 37a6a4ad..293523b1 100644 --- a/src/storages/inMemory/__tests__/EventsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/EventsCacheInMemory.spec.ts @@ -6,7 +6,7 @@ test('EVENTS CACHE / Should be able to instantiate and start with an empty queue const createInstance = () => cache = new EventsCacheInMemory(500); // 500 as eventsQueueSize expect(createInstance).not.toThrow(); // Creation should not throw. - expect(cache.state()).toEqual([]); // The queue starts empty. + expect(cache.pop()).toEqual([]); // The queue starts empty. }); test('EVENTS CACHE / Should be able to add items sequentially and retrieve the queue', () => { @@ -19,10 +19,11 @@ test('EVENTS CACHE / Should be able to add items sequentially and retrieve the q cache.track(queueValues[2]); cache.track(queueValues[3]); - const state = cache.state(); + const items = cache.pop(); - expect(state.length).toBe(4 /* pushed 4 items */); // The amount of items on queue should match the amount we pushed - expect(state).toEqual(queueValues); // The items should be in the queue and ordered as they were added. + expect(items.length).toBe(4 /* pushed 4 items */); // The amount of items on queue should match the amount we pushed + expect(items).toEqual(queueValues); // The items should be in the queue and ordered as they were added. + expect(cache.isEmpty()).toEqual(true); // Queue is empty }); test('EVENTS CACHE / Should be able to clear the queue and accumulated byte size', () => { @@ -31,20 +32,21 @@ test('EVENTS CACHE / Should be able to clear the queue and accumulated byte size cache.track('test1', 2019); cache.clear(); - expect(cache.state()).toEqual([]); // The queue should be clear. + expect(cache.pop()).toEqual([]); // The queue should be clear. expect(cache.queueByteSize).toBe(0); // The accumulated byte size should had been cleared. }); test('EVENTS CACHE / Should be able to tell if the queue is empty', () => { const cache = new EventsCacheInMemory(500); - expect(cache.state().length === 0).toBe(true); // The queue is empty, + expect(cache.pop().length).toBe(0); // The queue is empty, expect(cache.isEmpty()).toBe(true); // so if it is empty, it returns true. cache.track('test'); - - expect(cache.state().length > 0).toBe(true); // If we add something to the queue, expect(cache.isEmpty()).toBe(false); // it will return false. + + expect(cache.pop().length).toBe(1); // If we add something to the queue, + expect(cache.isEmpty()).toBe(true); // it will return true. }); test('EVENTS CACHE / Should be able to return the DTO we will send to BE', () => { @@ -56,8 +58,12 @@ test('EVENTS CACHE / Should be able to return the DTO we will send to BE', () => cache.track(queueValues[2]); cache.track(queueValues[3]); - const json = cache.state(); + const json = cache.pop(); expect(json).toEqual(queueValues); // For now the DTO is just an array of the saved events. + + // pop with merge + cache.track(0); cache.track(1); + expect(cache.pop([2, 3, 4])).toEqual([2, 3, 4, 0, 1]); }); test('EVENTS CACHE / Should call "onFullQueueCb" when the queue is full (count wise).', () => { diff --git a/src/storages/inMemory/__tests__/ImpressionCountsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/ImpressionCountsCacheInMemory.spec.ts index 00d878c9..b853bb2f 100644 --- a/src/storages/inMemory/__tests__/ImpressionCountsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/ImpressionCountsCacheInMemory.spec.ts @@ -20,12 +20,22 @@ test('IMPRESSION COUNTS CACHE / Impression Counter Test BasicUsage', () => { counter.track('feature2', timestamp + 3, 2); counter.track('feature2', timestamp + 4, 2); - const counted = counter.state(); + const counted = counter.pop(); expect(Object.keys(counted).length).toBe(2); expect(counted[counter._makeKey('feature1', timestamp)]).toBe(3); expect(counted[counter._makeKey('feature2', timestamp)]).toBe(4); + + // pop with merge + counter.track('feature1', timestamp, 1); + counter.track('feature3', timestamp, 10); + const countedWithMerge = counter.pop(counted); + expect(Object.keys(countedWithMerge).length).toBe(3); + expect(countedWithMerge[counter._makeKey('feature1', timestamp)]).toBe(4); + expect(countedWithMerge[counter._makeKey('feature2', timestamp)]).toBe(4); + expect(countedWithMerge[counter._makeKey('feature3', timestamp)]).toBe(10); + counter.clear(); - expect(Object.keys(counter.state()).length).toBe(0); + expect(Object.keys(counter.pop()).length).toBe(0); const nextHourTimestamp = new Date(2020, 9, 2, 11, 10, 12).getTime(); counter.track('feature1', timestamp, 1); @@ -38,12 +48,13 @@ test('IMPRESSION COUNTS CACHE / Impression Counter Test BasicUsage', () => { counter.track('feature1', nextHourTimestamp + 2, 1); counter.track('feature2', nextHourTimestamp + 3, 2); counter.track('feature2', nextHourTimestamp + 4, 2); - const counted2 = counter.state(); + expect(counter.isEmpty()).toBe(false); + const counted2 = counter.pop(); + expect(counter.isEmpty()).toBe(true); expect(Object.keys(counted2).length).toBe(4); expect(counted2[counter._makeKey('feature1', timestamp)]).toBe(3); expect(counted2[counter._makeKey('feature2', timestamp)]).toBe(4); expect(counted2[counter._makeKey('feature1', nextHourTimestamp)]).toBe(3); expect(counted2[counter._makeKey('feature2', nextHourTimestamp)]).toBe(4); - counter.clear(); - expect(Object.keys(counter.state()).length).toBe(0); + expect(Object.keys(counter.pop()).length).toBe(0); }); diff --git a/src/storages/inMemory/__tests__/ImpressionsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/ImpressionsCacheInMemory.spec.ts index 4a50eaf8..f90c1309 100644 --- a/src/storages/inMemory/__tests__/ImpressionsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/ImpressionsCacheInMemory.spec.ts @@ -5,20 +5,25 @@ test('IMPRESSIONS CACHE IN MEMORY / should incrementally store values, clear the const c = new ImpressionsCacheInMemory(); // queue is initially empty - expect(c.state()).toEqual([]); + expect(c.pop()).toEqual([]); expect(c.isEmpty()).toBe(true); - c.track([0]); c.track([1, 2]); c.track([3]); - expect(c.state()).toEqual([0, 1, 2, 3]); // all the items should be stored in sequential order expect(c.isEmpty()).toBe(false); + expect(c.pop()).toEqual([0, 1, 2, 3]); // all the items should be stored in sequential order + expect(c.isEmpty()).toBe(true); + + // pop with merge + c.track([0]); c.track([1]); + expect(c.pop([2, 3, 4])).toEqual([2, 3, 4, 0, 1]); // should empty the queue + c.track([0]); c.clear(); - expect(c.state()).toEqual([]); + expect(c.pop()).toEqual([]); expect(c.isEmpty()).toBe(true); }); diff --git a/src/storages/types.ts b/src/storages/types.ts index ea8616fa..1029ad62 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -301,8 +301,8 @@ export interface IRecorderCacheProducerSync { isEmpty(): boolean /* Clears cache data */ clear(): void - /* Gets cache data */ - state(): T + /* Pops cache data */ + pop(toMerge?: T): T } @@ -352,8 +352,7 @@ export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync // get cache data + pop(toMerge?: Record ): Record // pop cache data } diff --git a/src/sync/submitters/__tests__/eventsSubmitter.spec.ts b/src/sync/submitters/__tests__/eventsSubmitter.spec.ts index 16b26bc1..d8336b4f 100644 --- a/src/sync/submitters/__tests__/eventsSubmitter.spec.ts +++ b/src/sync/submitters/__tests__/eventsSubmitter.spec.ts @@ -1,6 +1,6 @@ import { eventsSubmitterFactory } from '../eventsSubmitter'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; - +import { EventsCacheInMemory } from '../../../storages/inMemory/EventsCacheInMemory'; describe('Events submitter', () => { @@ -16,7 +16,7 @@ describe('Events submitter', () => { scheduler: { eventsPushRate: 30000 }, startup: { eventsFirstPushWindow: 0 } }, - splitApi: { postEventsBulkMock: jest.fn() }, + splitApi: {}, storage: { events: eventsCacheMock } }; @@ -68,4 +68,36 @@ describe('Events submitter', () => { expect(eventsSubmitter.isRunning()).toEqual(false); }); + test('doesn\'t drop items from cache when POST is resolved', (done) => { + const eventsCacheInMemory = new EventsCacheInMemory(); + const params = { + settings: { log: loggerMock, scheduler: { eventsPushRate: 100 }, startup: { eventsFirstPushWindow: 0 } }, + storage: { events: eventsCacheInMemory }, + splitApi: { postEventsBulk: jest.fn(() => Promise.resolve()) }, + }; // @ts-ignore + const eventsSubmitter = eventsSubmitterFactory(params); + + eventsCacheInMemory.track({ eventTypeId: 'event1', timestamp: 1 }); + + eventsSubmitter.start(); + expect(params.splitApi.postEventsBulk.mock.calls).toEqual([['[{"eventTypeId":"event1","timestamp":1}]']]); + + // Tracking event when POST is pending + eventsCacheInMemory.track({ eventTypeId: 'event2', timestamp: 1 }); + // Tracking event when POST is resolved + setTimeout(() => { eventsCacheInMemory.track({ eventTypeId: 'event3', timestamp: 1 }); }); + + setTimeout(() => { + expect(params.splitApi.postEventsBulk.mock.calls).toEqual([ + // POST with event1 + ['[{"eventTypeId":"event1","timestamp":1}]'], + // POST with event2 and event3 + ['[{"eventTypeId":"event2","timestamp":1},{"eventTypeId":"event3","timestamp":1}]'] + ]); + eventsSubmitter.stop(); + + done(); + }, params.settings.scheduler.eventsPushRate + 10); + }); + }); diff --git a/src/sync/submitters/__tests__/impressionsSubmitter.spec.ts b/src/sync/submitters/__tests__/impressionsSubmitter.spec.ts new file mode 100644 index 00000000..4f59fb19 --- /dev/null +++ b/src/sync/submitters/__tests__/impressionsSubmitter.spec.ts @@ -0,0 +1,77 @@ +import { impressionsSubmitterFactory } from '../impressionsSubmitter'; +import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; +import { ImpressionsCacheInMemory } from '../../../storages/inMemory/ImpressionsCacheInMemory'; + +const imp1 = { + feature: 'someFeature', + keyName: 'k1', + changeNumber: 123, + label: 'someLabel', + treatment: 'someTreatment', + time: 0 +}; +const imp2 = { ...imp1, keyName: 'k2' }; +const imp3 = { ...imp1, keyName: 'k3' }; + +describe('Impressions submitter', () => { + + const impressionsCacheInMemory = new ImpressionsCacheInMemory(); + const params = { + settings: { log: loggerMock, scheduler: { impressionsPushRate: 100 }, core: {} }, + storage: { impressions: impressionsCacheInMemory }, + splitApi: { postTestImpressionsBulk: jest.fn(() => Promise.resolve()) }, + }; // @ts-ignore + const impressionsSubmitter = impressionsSubmitterFactory(params); + + beforeEach(() => { + params.splitApi.postTestImpressionsBulk.mockClear(); + }); + + test('doesn\'t drop items from cache when POST is resolved', (done) => { + + + impressionsCacheInMemory.track([imp1]); + impressionsSubmitter.start(); + + // Tracking impression when POST is pending + impressionsCacheInMemory.track([imp2]); + // Tracking impression after POST is resolved + setTimeout(() => { impressionsCacheInMemory.track([imp3]); }); + + setTimeout(() => { + expect(params.splitApi.postTestImpressionsBulk.mock.calls).toEqual([ + // POST with imp1 + ['[{"f":"someFeature","i":[{"k":"k1","t":"someTreatment","m":0,"c":123}]}]'], + // POST with imp2 and imp3 + ['[{"f":"someFeature","i":[{"k":"k2","t":"someTreatment","m":0,"c":123},{"k":"k3","t":"someTreatment","m":0,"c":123}]}]']]); + impressionsSubmitter.stop(); + + done(); + }, params.settings.scheduler.impressionsPushRate + 10); + }); + + test('in case of retry, pop new items from cache to include in the POST payload', (done) => { + // Make the POST request fail + params.splitApi.postTestImpressionsBulk.mockImplementation(() => Promise.reject()); + + impressionsCacheInMemory.track([imp1]); + impressionsSubmitter.start(); + + // Tracking impression when POST is pending + impressionsCacheInMemory.track([imp2]); + // Tracking impression after POST is rejected + setTimeout(() => { impressionsCacheInMemory.track([imp3]); }); + + setTimeout(() => { + expect(params.splitApi.postTestImpressionsBulk.mock.calls).toEqual([ + // impression for imp1 + ['[{"f":"someFeature","i":[{"k":"k1","t":"someTreatment","m":0,"c":123}]}]'], + // impressions for imp1, imp2 and imp3 + ['[{"f":"someFeature","i":[{"k":"k1","t":"someTreatment","m":0,"c":123},{"k":"k2","t":"someTreatment","m":0,"c":123},{"k":"k3","t":"someTreatment","m":0,"c":123}]}]']]); + impressionsSubmitter.stop(); + + done(); + }, params.settings.scheduler.impressionsPushRate + 10); + }); + +}); diff --git a/src/sync/submitters/submitter.ts b/src/sync/submitters/submitter.ts index a1312c7d..2cf16d7c 100644 --- a/src/sync/submitters/submitter.ts +++ b/src/sync/submitters/submitter.ts @@ -8,39 +8,39 @@ import { IResponse } from '../../services/types'; /** * Base function to create submitters, such as ImpressionsSubmitter and EventsSubmitter */ -export function submitterFactory( +export function submitterFactory( log: ILogger, postClient: (body: string) => Promise, - sourceCache: IRecorderCacheProducerSync, + sourceCache: IRecorderCacheProducerSync, postRate: number, dataName: string, - fromCacheToPayload?: (cacheData: TState) => any, + fromCacheToPayload?: (cacheData: T) => any, maxRetries: number = 0, debugLogs?: boolean // true for telemetry submitters ): ISyncTask<[], void> { let retries = 0; + let data: any; function postData(): Promise { - if (sourceCache.isEmpty()) return Promise.resolve(); + if (sourceCache.isEmpty() && !data) return Promise.resolve(); + data = sourceCache.pop(data); - const data = sourceCache.state(); - // @ts-ignore const dataCountMessage = typeof data.length === 'number' ? `${data.length} ${dataName}` : dataName; log[debugLogs ? 'debug' : 'info'](SUBMITTERS_PUSH, [dataCountMessage]); const jsonPayload = JSON.stringify(fromCacheToPayload ? fromCacheToPayload(data) : data); - if (!maxRetries) sourceCache.clear(); + if (!maxRetries) data = undefined; return postClient(jsonPayload).then(() => { retries = 0; - sourceCache.clear(); // we clear the queue if request successes. + data = undefined; }).catch(err => { if (!maxRetries) { log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]); } else if (retries === maxRetries) { retries = 0; - sourceCache.clear(); // we clear the queue if request fails after retries. + data = undefined; log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]); } else { retries++; diff --git a/src/sync/submitters/telemetrySubmitter.ts b/src/sync/submitters/telemetrySubmitter.ts index 8e208e59..e23f0d6c 100644 --- a/src/sync/submitters/telemetrySubmitter.ts +++ b/src/sync/submitters/telemetrySubmitter.ts @@ -19,7 +19,7 @@ export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, split clear() { }, // No-op // @TODO consider moving inside telemetry cache for code size reduction - state(): TelemetryUsageStatsPayload { + pop(): TelemetryUsageStatsPayload { return { lS: telemetry.getLastSynchronization(), mL: telemetry.popLatencies(), @@ -88,7 +88,7 @@ export function telemetryCacheConfigAdapter(telemetry: ITelemetryCacheSync, sett isEmpty() { return false; }, clear() { }, - state(): TelemetryConfigStatsPayload { + pop(): TelemetryConfigStatsPayload { const { urls, scheduler } = settings; const isClientSide = settings.core.key !== undefined; diff --git a/src/trackers/__tests__/impressionsTracker.spec.ts b/src/trackers/__tests__/impressionsTracker.spec.ts index 5ca63895..ff0d8368 100644 --- a/src/trackers/__tests__/impressionsTracker.spec.ts +++ b/src/trackers/__tests__/impressionsTracker.spec.ts @@ -188,7 +188,7 @@ describe('Impressions Tracker', () => { expect(lastArgs[0][1].pt).toBe(undefined); expect(lastArgs[0][1].feature).toBe('qc_team_2'); - expect(Object.keys(impressionCountsCache.state()).length).toBe(2); + expect(Object.keys(impressionCountsCache.pop()).length).toBe(2); expect(fakeTelemetryCache.recordImpressionStats.mock.calls).toEqual([[QUEUED, 2], [DEDUPED, 1]]); });