diff --git a/src/listeners/browser.ts b/src/listeners/browser.ts index a43ed22c..0a1616d1 100644 --- a/src/listeners/browser.ts +++ b/src/listeners/browser.ts @@ -9,7 +9,7 @@ import { ISettings } from '../types'; import SplitIO from '../../types/splitio'; import { ImpressionsPayload } from '../sync/submitters/types'; import { objectAssign } from '../utils/lang/objectAssign'; -import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants'; +import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING, SUBMITTERS_PUSH_PAGE_HIDDEN } from '../logger/constants'; import { ISyncManager } from '../sync/types'; import { isConsentGranted } from '../consent'; @@ -104,6 +104,7 @@ export class BrowserSignalListener implements ISignalListener { if (!this._sendBeacon(url, dataPayload, extraMetadata)) { postService(JSON.stringify(dataPayload)).catch(() => { }); // no-op to handle possible promise rejection } + this.settings.log.debug(SUBMITTERS_PUSH_PAGE_HIDDEN, [cache.name]); } } diff --git a/src/logger/constants.ts b/src/logger/constants.ts index 729da6e1..de1ebe58 100644 --- a/src/logger/constants.ts +++ b/src/logger/constants.ts @@ -55,6 +55,7 @@ export const IMPRESSIONS_TRACKER_SUCCESS = 121; export const USER_CONSENT_UPDATED = 122; export const USER_CONSENT_NOT_UPDATED = 123; export const USER_CONSENT_INITIAL = 124; +export const SUBMITTERS_PUSH_PAGE_HIDDEN = 125; export const ENGINE_VALUE_INVALID = 200; export const ENGINE_VALUE_NO_ATTRIBUTES = 201; diff --git a/src/logger/messages/info.ts b/src/logger/messages/info.ts index fc688224..fb017250 100644 --- a/src/logger/messages/info.ts +++ b/src/logger/messages/info.ts @@ -25,6 +25,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([ [c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying download of feature flags #%s. Reason: %s'], [c.SUBMITTERS_PUSH_FULL_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full %s queue and resetting timer.'], [c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s.'], + [c.SUBMITTERS_PUSH_PAGE_HIDDEN, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing %s because page became hidden.'], [c.STREAMING_REFRESH_TOKEN, c.LOG_PREFIX_SYNC_STREAMING + 'Refreshing streaming token in %s seconds, and connecting streaming in %s seconds.'], [c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect streaming in %s seconds.'], [c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting streaming.'], diff --git a/src/storages/inMemory/EventsCacheInMemory.ts b/src/storages/inMemory/EventsCacheInMemory.ts index 5c897868..bdfd6e7c 100644 --- a/src/storages/inMemory/EventsCacheInMemory.ts +++ b/src/storages/inMemory/EventsCacheInMemory.ts @@ -5,6 +5,7 @@ const MAX_QUEUE_BYTE_SIZE = 5 * 1024 * 1024; // 5M export class EventsCacheInMemory implements IEventsCacheSync { + public name = 'events'; private onFullQueue?: () => void; private readonly maxQueue: number; private queue: SplitIO.EventData[]; diff --git a/src/storages/inMemory/ImpressionCountsCacheInMemory.ts b/src/storages/inMemory/ImpressionCountsCacheInMemory.ts index fab077cd..28951357 100644 --- a/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +++ b/src/storages/inMemory/ImpressionCountsCacheInMemory.ts @@ -3,6 +3,8 @@ import { DEFAULT_CACHE_SIZE } from '../inRedis/constants'; import { IImpressionCountsCacheSync } from '../types'; export class ImpressionCountsCacheInMemory implements IImpressionCountsCacheSync { + + public name = 'impression counts'; protected cache: Record = {}; private readonly maxStorage: number; protected onFullQueue?: () => void; diff --git a/src/storages/inMemory/ImpressionsCacheInMemory.ts b/src/storages/inMemory/ImpressionsCacheInMemory.ts index 6995481a..65e2b886 100644 --- a/src/storages/inMemory/ImpressionsCacheInMemory.ts +++ b/src/storages/inMemory/ImpressionsCacheInMemory.ts @@ -3,6 +3,7 @@ import SplitIO from '../../../types/splitio'; export class ImpressionsCacheInMemory implements IImpressionsCacheSync { + public name = 'impressions'; private onFullQueue?: () => void; private readonly maxQueue: number; private queue: SplitIO.ImpressionDTO[]; diff --git a/src/storages/inMemory/TelemetryCacheInMemory.ts b/src/storages/inMemory/TelemetryCacheInMemory.ts index 7e7e3f98..29b38a15 100644 --- a/src/storages/inMemory/TelemetryCacheInMemory.ts +++ b/src/storages/inMemory/TelemetryCacheInMemory.ts @@ -25,6 +25,8 @@ export function shouldRecordTelemetry({ settings }: IStorageFactoryParams) { export class TelemetryCacheInMemory implements ITelemetryCacheSync { + public name = 'telemetry stats'; + constructor(private splits?: ISplitsCacheSync, private segments?: ISegmentsCacheSync, private largeSegments?: ISegmentsCacheSync) { } // isEmpty flag diff --git a/src/storages/inMemory/UniqueKeysCacheInMemory.ts b/src/storages/inMemory/UniqueKeysCacheInMemory.ts index 9c45721c..d5728d2a 100644 --- a/src/storages/inMemory/UniqueKeysCacheInMemory.ts +++ b/src/storages/inMemory/UniqueKeysCacheInMemory.ts @@ -1,4 +1,4 @@ -import { IUniqueKeysCacheBase } from '../types'; +import { IUniqueKeysCacheSync } from '../types'; import { UniqueKeysPayloadSs } from '../../sync/submitters/types'; import { DEFAULT_CACHE_SIZE } from '../inRedis/constants'; import { setToArray } from '../../utils/lang/sets'; @@ -22,8 +22,8 @@ export function fromUniqueKeysCollector(uniqueKeys: { [featureName: string]: Set return { keys: payload }; } -export class UniqueKeysCacheInMemory implements IUniqueKeysCacheBase { - +export class UniqueKeysCacheInMemory implements IUniqueKeysCacheSync { + public name = 'unique keys'; protected onFullQueue?: () => void; private readonly maxStorage: number; private uniqueTrackerSize = 0; diff --git a/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts b/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts index 87133e3d..32ba603d 100644 --- a/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +++ b/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts @@ -1,10 +1,10 @@ -import { IUniqueKeysCacheBase } from '../types'; +import { IUniqueKeysCacheSync } from '../types'; import { UniqueKeysPayloadCs } from '../../sync/submitters/types'; import { DEFAULT_CACHE_SIZE } from '../inRedis/constants'; import { setToArray } from '../../utils/lang/sets'; -export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheBase { - +export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheSync { + public name = 'unique keys'; private onFullQueue?: () => void; private readonly maxStorage: number; private uniqueTrackerSize = 0; diff --git a/src/storages/types.ts b/src/storages/types.ts index 0fbbc4d7..3662047f 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -325,6 +325,7 @@ export interface IUniqueKeysCacheBase { // API methods for sync recorder storages, used by submitters in standalone mode to pop data and post it to Split BE. export interface IRecorderCacheSync { + name: string, // @TODO names are inconsistent with spec /* Checks if cache is empty. Returns true if the cache was just created or cleared */ isEmpty(): boolean diff --git a/src/sync/submitters/eventsSubmitter.ts b/src/sync/submitters/eventsSubmitter.ts index 998b7aba..20579a7c 100644 --- a/src/sync/submitters/eventsSubmitter.ts +++ b/src/sync/submitters/eventsSubmitter.ts @@ -2,8 +2,6 @@ import { submitterFactory, firstPushWindowDecorator } from './submitter'; import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants'; import { ISdkFactoryContextSync } from '../../sdkFactory/types'; -const DATA_NAME = 'events'; - /** * Submitter that periodically posts tracked events */ @@ -16,7 +14,7 @@ export function eventsSubmitterFactory(params: ISdkFactoryContextSync) { } = params; // don't retry events. - let submitter = submitterFactory(log, postEventsBulk, events, eventsPushRate, DATA_NAME); + let submitter = submitterFactory(log, postEventsBulk, events, eventsPushRate); // Set a timer for the first push window of events. if (eventsFirstPushWindow > 0) submitter = firstPushWindowDecorator(submitter, eventsFirstPushWindow); @@ -24,7 +22,7 @@ export function eventsSubmitterFactory(params: ISdkFactoryContextSync) { // register events submitter to be executed when events cache is full events.setOnFullQueueCb(() => { if (submitter.isRunning()) { - log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); + log.info(SUBMITTERS_PUSH_FULL_QUEUE, [events.name]); submitter.execute(); } // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data. diff --git a/src/sync/submitters/impressionCountsSubmitter.ts b/src/sync/submitters/impressionCountsSubmitter.ts index fb02045c..6905efa9 100644 --- a/src/sync/submitters/impressionCountsSubmitter.ts +++ b/src/sync/submitters/impressionCountsSubmitter.ts @@ -40,5 +40,5 @@ export function impressionCountsSubmitterFactory(params: ISdkFactoryContextSync) } = params; // retry impressions counts only once. - return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1); + return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, fromImpressionCountsCollector, 1); } diff --git a/src/sync/submitters/impressionsSubmitter.ts b/src/sync/submitters/impressionsSubmitter.ts index bf05a587..2fa85ab4 100644 --- a/src/sync/submitters/impressionsSubmitter.ts +++ b/src/sync/submitters/impressionsSubmitter.ts @@ -5,8 +5,6 @@ import { ImpressionsPayload } from './types'; import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants'; import { ISdkFactoryContextSync } from '../../sdkFactory/types'; -const DATA_NAME = 'impressions'; - /** * Converts `impressions` data from cache into request payload. */ @@ -14,7 +12,6 @@ export function fromImpressionsCollector(sendLabels: boolean, data: SplitIO.Impr let groupedByFeature = groupBy(data, 'feature'); let dto: ImpressionsPayload = []; - // using forOwn instead of for...in since the last also iterates over prototype enumerables forOwn(groupedByFeature, (value, name) => { dto.push({ f: name, // Test Name @@ -50,12 +47,12 @@ export function impressionsSubmitterFactory(params: ISdkFactoryContextSync) { } = params; // retry impressions only once. - const syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, DATA_NAME, fromImpressionsCollector.bind(undefined, labelsEnabled), 1); + const syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, fromImpressionsCollector.bind(undefined, labelsEnabled), 1); // register impressions submitter to be executed when impressions cache is full impressions.setOnFullQueueCb(() => { if (syncTask.isRunning()) { - log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); + log.info(SUBMITTERS_PUSH_FULL_QUEUE, [impressions.name]); syncTask.execute(); } // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data. diff --git a/src/sync/submitters/submitter.ts b/src/sync/submitters/submitter.ts index da702e9e..65957ed6 100644 --- a/src/sync/submitters/submitter.ts +++ b/src/sync/submitters/submitter.ts @@ -13,12 +13,12 @@ export function submitterFactory( postClient: (body: string) => Promise, sourceCache: IRecorderCacheSync, postRate: number, - dataName: string, fromCacheToPayload?: (cacheData: T) => any, maxRetries: number = 0, debugLogs?: boolean // true for telemetry submitters ): ISyncTask<[], void> { + const dataName = sourceCache.name; let retries = 0; let data: any; diff --git a/src/sync/submitters/telemetrySubmitter.ts b/src/sync/submitters/telemetrySubmitter.ts index 7a2e2ee7..82fba1c5 100644 --- a/src/sync/submitters/telemetrySubmitter.ts +++ b/src/sync/submitters/telemetrySubmitter.ts @@ -66,6 +66,7 @@ export function getTelemetryConfigStats(mode: SplitIO.SDKMode, storageType: stri */ export function telemetryCacheConfigAdapter(telemetry: ITelemetryCacheSync, settings: ISettings) { return { + name: 'telemetry config', isEmpty() { return false; }, clear() { }, @@ -124,7 +125,7 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) { submitterFactory( log, splitApi.postMetricsUsage, telemetry, - telemetryRefreshRate, 'telemetry stats', undefined, 0, true + telemetryRefreshRate, undefined, 0, true ), telemetryRefreshRate ); @@ -139,7 +140,7 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) { // Post config data when the SDK is ready and if the telemetry submitter was started if (submitter.isRunning()) { - const postMetricsConfigTask = submitterFactory(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0, 'telemetry config', undefined, 0, true); + const postMetricsConfigTask = submitterFactory(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0, undefined, 0, true); postMetricsConfigTask.execute(); } }); diff --git a/src/sync/submitters/uniqueKeysSubmitter.ts b/src/sync/submitters/uniqueKeysSubmitter.ts index f10a1aca..33f3e816 100644 --- a/src/sync/submitters/uniqueKeysSubmitter.ts +++ b/src/sync/submitters/uniqueKeysSubmitter.ts @@ -2,7 +2,6 @@ import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants'; import { ISdkFactoryContextSync } from '../../sdkFactory/types'; import { submitterFactory } from './submitter'; -const DATA_NAME = 'unique keys'; const UNIQUE_KEYS_RATE = 900000; // 15 minutes /** @@ -19,12 +18,12 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) { const isClientSide = key !== undefined; const postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs; - const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys, UNIQUE_KEYS_RATE, DATA_NAME); + const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys, UNIQUE_KEYS_RATE); // register unique keys submitter to be executed when uniqueKeys cache is full uniqueKeys.setOnFullQueueCb(() => { if (syncTask.isRunning()) { - log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); + log.info(SUBMITTERS_PUSH_FULL_QUEUE, [uniqueKeys.name]); syncTask.execute(); } // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.