@splitsoftware/splitio-commons 1.6.2-rc.9 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +6 -1
- package/cjs/consent/sdkUserConsent.js +2 -2
- package/cjs/listeners/browser.js +11 -12
- package/cjs/logger/constants.js +2 -1
- package/cjs/sdkClient/sdkClient.js +3 -1
- package/cjs/sdkFactory/index.js +26 -26
- package/cjs/services/splitApi.js +20 -0
- package/cjs/storages/AbstractSplitsCacheAsync.js +1 -1
- package/cjs/storages/AbstractSplitsCacheSync.js +1 -1
- package/cjs/storages/KeyBuilderSS.js +10 -43
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +0 -1
- package/cjs/storages/inLocalStorage/index.js +17 -9
- package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
- package/cjs/storages/inMemory/InMemoryStorage.js +13 -6
- package/cjs/storages/inMemory/InMemoryStorageCS.js +13 -6
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +60 -35
- package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +72 -0
- package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +76 -0
- package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +85 -0
- package/cjs/storages/inRedis/ImpressionsCacheInRedis.js +2 -19
- package/cjs/storages/inRedis/TelemetryCacheInRedis.js +4 -4
- package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +71 -0
- package/cjs/storages/inRedis/constants.js +4 -1
- package/cjs/storages/inRedis/index.js +20 -3
- package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +81 -0
- package/cjs/storages/pluggable/ImpressionsCachePluggable.js +2 -19
- package/cjs/storages/pluggable/TelemetryCachePluggable.js +4 -4
- package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +61 -0
- package/cjs/storages/pluggable/inMemoryWrapper.js +8 -6
- package/cjs/storages/pluggable/index.js +38 -9
- package/cjs/storages/utils.js +73 -0
- package/cjs/sync/submitters/submitterManager.js +3 -0
- package/cjs/sync/submitters/telemetrySubmitter.js +5 -40
- package/cjs/sync/submitters/uniqueKeysSubmitter.js +27 -0
- package/cjs/trackers/impressionObserver/utils.js +1 -17
- package/cjs/trackers/impressionsTracker.js +22 -41
- package/cjs/trackers/strategy/strategyDebug.js +25 -0
- package/cjs/trackers/strategy/strategyNone.js +29 -0
- package/cjs/trackers/strategy/strategyOptimized.js +35 -0
- package/cjs/trackers/uniqueKeysTracker.js +38 -0
- package/cjs/utils/constants/index.js +4 -2
- package/cjs/utils/redis/RedisMock.js +31 -0
- package/cjs/utils/settingsValidation/impressionsMode.js +2 -2
- package/cjs/utils/settingsValidation/index.js +9 -3
- package/esm/consent/sdkUserConsent.js +2 -2
- package/esm/listeners/browser.js +12 -13
- package/esm/logger/constants.js +1 -0
- package/esm/sdkClient/sdkClient.js +3 -1
- package/esm/sdkFactory/index.js +26 -26
- package/esm/services/splitApi.js +20 -0
- package/esm/storages/AbstractSplitsCacheAsync.js +1 -1
- package/esm/storages/AbstractSplitsCacheSync.js +1 -1
- package/esm/storages/KeyBuilderSS.js +7 -37
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +0 -1
- package/esm/storages/inLocalStorage/index.js +18 -10
- package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
- package/esm/storages/inMemory/InMemoryStorage.js +14 -7
- package/esm/storages/inMemory/InMemoryStorageCS.js +14 -7
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +61 -36
- package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +68 -0
- package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +73 -0
- package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +82 -0
- package/esm/storages/inRedis/ImpressionsCacheInRedis.js +2 -19
- package/esm/storages/inRedis/TelemetryCacheInRedis.js +1 -1
- package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +68 -0
- package/esm/storages/inRedis/constants.js +3 -0
- package/esm/storages/inRedis/index.js +21 -4
- package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +78 -0
- package/esm/storages/pluggable/ImpressionsCachePluggable.js +2 -19
- package/esm/storages/pluggable/TelemetryCachePluggable.js +1 -1
- package/esm/storages/pluggable/UniqueKeysCachePluggable.js +58 -0
- package/esm/storages/pluggable/inMemoryWrapper.js +8 -6
- package/esm/storages/pluggable/index.js +39 -10
- package/esm/storages/utils.js +65 -0
- package/esm/sync/submitters/submitterManager.js +3 -0
- package/esm/sync/submitters/telemetrySubmitter.js +5 -39
- package/esm/sync/submitters/uniqueKeysSubmitter.js +23 -0
- package/esm/trackers/impressionObserver/utils.js +1 -15
- package/esm/trackers/impressionsTracker.js +22 -41
- package/esm/trackers/strategy/strategyDebug.js +21 -0
- package/esm/trackers/strategy/strategyNone.js +25 -0
- package/esm/trackers/strategy/strategyOptimized.js +31 -0
- package/esm/trackers/uniqueKeysTracker.js +34 -0
- package/esm/utils/constants/index.js +2 -0
- package/esm/utils/redis/RedisMock.js +28 -0
- package/esm/utils/settingsValidation/impressionsMode.js +3 -3
- package/esm/utils/settingsValidation/index.js +9 -3
- package/package.json +1 -2
- package/src/consent/sdkUserConsent.ts +2 -2
- package/src/listeners/browser.ts +12 -15
- package/src/logger/constants.ts +1 -0
- package/src/sdkClient/sdkClient.ts +3 -1
- package/src/sdkFactory/index.ts +29 -31
- package/src/sdkFactory/types.ts +7 -4
- package/src/services/splitApi.ts +22 -0
- package/src/services/types.ts +6 -0
- package/src/storages/AbstractSplitsCacheAsync.ts +1 -1
- package/src/storages/AbstractSplitsCacheSync.ts +1 -1
- package/src/storages/KeyBuilderSS.ts +9 -43
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +0 -1
- package/src/storages/inLocalStorage/index.ts +18 -10
- package/src/storages/inMemory/AttributesCacheInMemory.ts +7 -7
- package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +16 -1
- package/src/storages/inMemory/InMemoryStorage.ts +14 -7
- package/src/storages/inMemory/InMemoryStorageCS.ts +14 -7
- package/src/storages/inMemory/TelemetryCacheInMemory.ts +69 -34
- package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +80 -0
- package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +86 -0
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +95 -0
- package/src/storages/inRedis/ImpressionsCacheInRedis.ts +2 -22
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +3 -2
- package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +77 -0
- package/src/storages/inRedis/constants.ts +3 -0
- package/src/storages/inRedis/index.ts +18 -5
- package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +92 -0
- package/src/storages/pluggable/ImpressionsCachePluggable.ts +3 -23
- package/src/storages/pluggable/TelemetryCachePluggable.ts +3 -2
- package/src/storages/pluggable/UniqueKeysCachePluggable.ts +67 -0
- package/src/storages/pluggable/inMemoryWrapper.ts +6 -6
- package/src/storages/pluggable/index.ts +41 -9
- package/src/storages/types.ts +56 -55
- package/src/storages/utils.ts +78 -0
- package/src/sync/submitters/submitter.ts +2 -2
- package/src/sync/submitters/submitterManager.ts +2 -0
- package/src/sync/submitters/telemetrySubmitter.ts +8 -43
- package/src/sync/submitters/types.ts +29 -27
- package/src/sync/submitters/uniqueKeysSubmitter.ts +36 -0
- package/src/trackers/impressionObserver/utils.ts +1 -16
- package/src/trackers/impressionsTracker.ts +25 -46
- package/src/trackers/strategy/strategyDebug.ts +28 -0
- package/src/trackers/strategy/strategyNone.ts +34 -0
- package/src/trackers/strategy/strategyOptimized.ts +42 -0
- package/src/trackers/types.ts +28 -0
- package/src/trackers/uniqueKeysTracker.ts +48 -0
- package/src/types.ts +1 -1
- package/src/utils/constants/index.ts +2 -0
- package/src/utils/redis/RedisMock.ts +33 -0
- package/src/utils/settingsValidation/impressionsMode.ts +3 -3
- package/src/utils/settingsValidation/index.ts +7 -3
- package/types/logger/constants.d.ts +1 -0
- package/types/sdkFactory/types.d.ts +4 -2
- package/types/services/types.d.ts +4 -0
- package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
- package/types/storages/AbstractSplitsCacheSync.d.ts +1 -1
- package/types/storages/KeyBuilderSS.d.ts +3 -3
- package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +0 -1
- package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +5 -1
- package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +20 -9
- package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +35 -0
- package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +35 -0
- package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +16 -0
- package/types/storages/inRedis/ImpressionsCacheInRedis.d.ts +0 -1
- package/types/storages/inRedis/constants.d.ts +3 -0
- package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +21 -0
- package/types/storages/pluggable/ImpressionCountsCachePluggable.d.ts +16 -0
- package/types/storages/pluggable/ImpressionsCachePluggable.d.ts +1 -2
- package/types/storages/pluggable/UniqueKeysCachePluggable.d.ts +20 -0
- package/types/storages/types.d.ts +39 -38
- package/types/storages/utils.d.ts +8 -0
- package/types/sync/submitters/submitter.d.ts +2 -2
- package/types/sync/submitters/telemetrySubmitter.d.ts +2 -10
- package/types/sync/submitters/types.d.ts +27 -7
- package/types/sync/submitters/uniqueKeysSubmitter.d.ts +5 -0
- package/types/trackers/impressionObserver/utils.d.ts +0 -8
- package/types/trackers/impressionsTracker.d.ts +4 -6
- package/types/trackers/strategy/strategyDebug.d.ts +9 -0
- package/types/trackers/strategy/strategyNone.d.ts +10 -0
- package/types/trackers/strategy/strategyOptimized.d.ts +11 -0
- package/types/trackers/types.d.ts +23 -0
- package/types/trackers/uniqueKeysTracker.d.ts +13 -0
- package/types/types.d.ts +1 -1
- package/types/utils/constants/index.d.ts +2 -0
- package/types/utils/redis/RedisMock.d.ts +4 -0
- package/cjs/storages/metadataBuilder.js +0 -12
- package/esm/storages/metadataBuilder.js +0 -8
- package/src/storages/metadataBuilder.ts +0 -11
|
@@ -8,12 +8,17 @@ import { EventsCachePluggable } from './EventsCachePluggable';
|
|
|
8
8
|
import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter';
|
|
9
9
|
import { isObject } from '../../utils/lang';
|
|
10
10
|
import { validatePrefix } from '../KeyBuilder';
|
|
11
|
-
import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
|
|
11
|
+
import { CONSUMER_PARTIAL_MODE, DEBUG, NONE, STORAGE_PLUGGABLE } from '../../utils/constants';
|
|
12
12
|
import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory';
|
|
13
13
|
import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
|
|
14
14
|
import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
|
|
15
15
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
|
|
16
16
|
import { TelemetryCachePluggable } from './TelemetryCachePluggable';
|
|
17
|
+
import { ImpressionCountsCachePluggable } from './ImpressionCountsCachePluggable';
|
|
18
|
+
import { UniqueKeysCachePluggable } from './UniqueKeysCachePluggable';
|
|
19
|
+
import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
|
|
20
|
+
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
|
|
21
|
+
import { metadataBuilder } from '../utils';
|
|
17
22
|
|
|
18
23
|
const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
|
|
19
24
|
const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
|
|
@@ -57,19 +62,42 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
57
62
|
const prefix = validatePrefix(options.prefix);
|
|
58
63
|
|
|
59
64
|
function PluggableStorageFactory(params: IStorageFactoryParams): IStorageAsync {
|
|
60
|
-
const {
|
|
65
|
+
const { onReadyCb, settings, settings: { log, mode, sync: { impressionsMode }, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
|
|
66
|
+
const metadata = metadataBuilder(settings);
|
|
61
67
|
const keys = new KeyBuilderSS(prefix, metadata);
|
|
62
68
|
const wrapper = wrapperAdapter(log, options.wrapper);
|
|
69
|
+
|
|
70
|
+
const isSyncronizer = mode === undefined; // If mode is not defined, the synchronizer is running
|
|
63
71
|
const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
|
|
64
|
-
|
|
65
|
-
|
|
72
|
+
|
|
73
|
+
const telemetry = shouldRecordTelemetry(params) || isSyncronizer ?
|
|
74
|
+
isPartialConsumer ?
|
|
75
|
+
new TelemetryCacheInMemory() :
|
|
76
|
+
new TelemetryCachePluggable(log, keys, wrapper) :
|
|
77
|
+
undefined;
|
|
78
|
+
|
|
79
|
+
const impressionCountsCache = impressionsMode !== DEBUG || isSyncronizer ?
|
|
80
|
+
isPartialConsumer ?
|
|
81
|
+
new ImpressionCountsCacheInMemory() :
|
|
82
|
+
new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper) :
|
|
83
|
+
undefined;
|
|
84
|
+
|
|
85
|
+
const uniqueKeysCache = impressionsMode === NONE || isSyncronizer ?
|
|
86
|
+
isPartialConsumer ?
|
|
87
|
+
settings.core.key === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
|
|
88
|
+
new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
|
|
66
89
|
undefined;
|
|
67
90
|
|
|
68
91
|
// Connects to wrapper and emits SDK_READY event on main client
|
|
69
92
|
const connectPromise = wrapper.connect().then(() => {
|
|
70
93
|
onReadyCb();
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
|
|
95
|
+
// Start periodic flush of async storages if not running synchronizer (producer mode)
|
|
96
|
+
if (!isSyncronizer) {
|
|
97
|
+
if (impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
|
|
98
|
+
if (uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
|
|
99
|
+
if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig) (telemetry as ITelemetryCacheAsync).recordConfig();
|
|
100
|
+
}
|
|
73
101
|
}).catch((e) => {
|
|
74
102
|
e = e || new Error('Error connecting wrapper');
|
|
75
103
|
onReadyCb(e);
|
|
@@ -80,13 +108,17 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
80
108
|
splits: new SplitsCachePluggable(log, keys, wrapper),
|
|
81
109
|
segments: new SegmentsCachePluggable(log, keys, wrapper),
|
|
82
110
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
83
|
-
impressionCounts:
|
|
111
|
+
impressionCounts: impressionCountsCache,
|
|
84
112
|
events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
|
|
85
113
|
telemetry,
|
|
114
|
+
uniqueKeys: uniqueKeysCache,
|
|
86
115
|
|
|
87
|
-
//
|
|
116
|
+
// Stop periodic flush and disconnect the underlying storage
|
|
88
117
|
destroy() {
|
|
89
|
-
return
|
|
118
|
+
return Promise.all(isSyncronizer ? [] : [
|
|
119
|
+
impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
|
|
120
|
+
uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
|
|
121
|
+
]).then(() => wrapper.disconnect());
|
|
90
122
|
},
|
|
91
123
|
|
|
92
124
|
// emits SDK_READY event on shared clients and returns a reference to the storage
|
package/src/storages/types.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { MaybeThenable,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { SplitIO, ImpressionDTO, SDKMode } from '../types';
|
|
1
|
+
import { MaybeThenable, ISplit } from '../dtos/types';
|
|
2
|
+
import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs, TelemetryUsageStatsPayload } from '../sync/submitters/types';
|
|
3
|
+
import { SplitIO, ImpressionDTO, ISettings } from '../types';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Interface of a pluggable storage wrapper.
|
|
@@ -70,23 +69,25 @@ export interface IPluggableStorageWrapper {
|
|
|
70
69
|
/** Integer operations */
|
|
71
70
|
|
|
72
71
|
/**
|
|
73
|
-
* Increments
|
|
72
|
+
* Increments the number stored at `key` by `increment`, or set it to `increment` if the value doesn't exist.
|
|
74
73
|
*
|
|
75
74
|
* @function incr
|
|
76
75
|
* @param {string} key Key to increment
|
|
76
|
+
* @param {number} increment Value to increment by. Defaults to 1.
|
|
77
77
|
* @returns {Promise<number>} A promise that resolves with the value of key after the increment. The promise rejects if the operation fails,
|
|
78
78
|
* for example, if there is a connection error or the key contains a string that can not be represented as integer.
|
|
79
79
|
*/
|
|
80
|
-
incr: (key: string) => Promise<number>
|
|
80
|
+
incr: (key: string, increment?: number) => Promise<number>
|
|
81
81
|
/**
|
|
82
|
-
* Decrements
|
|
82
|
+
* Decrements the number stored at `key` by `decrement`, or set it to minus `decrement` if the value doesn't exist.
|
|
83
83
|
*
|
|
84
84
|
* @function decr
|
|
85
85
|
* @param {string} key Key to decrement
|
|
86
|
+
* @param {number} decrement Value to decrement by. Defaults to 1.
|
|
86
87
|
* @returns {Promise<number>} A promise that resolves with the value of key after the decrement. The promise rejects if the operation fails,
|
|
87
88
|
* for example, if there is a connection error or the key contains a string that can not be represented as integer.
|
|
88
89
|
*/
|
|
89
|
-
decr: (key: string) => Promise<number>
|
|
90
|
+
decr: (key: string, decrement?: number) => Promise<number>
|
|
90
91
|
|
|
91
92
|
/** Queue operations */
|
|
92
93
|
|
|
@@ -283,19 +284,29 @@ export interface ISegmentsCacheAsync extends ISegmentsCacheBase {
|
|
|
283
284
|
/** Recorder storages (impressions, events and telemetry) */
|
|
284
285
|
|
|
285
286
|
export interface IImpressionsCacheBase {
|
|
286
|
-
//
|
|
287
|
+
// Used by impressions tracker, in DEBUG and OPTIMIZED impression modes, to push impressions into the storage.
|
|
287
288
|
track(data: ImpressionDTO[]): MaybeThenable<void>
|
|
288
289
|
}
|
|
289
290
|
|
|
290
291
|
export interface IEventsCacheBase {
|
|
291
|
-
//
|
|
292
|
+
// Used by events tracker to push events into the storage.
|
|
292
293
|
track(data: SplitIO.EventData, size?: number): MaybeThenable<boolean>
|
|
293
294
|
}
|
|
294
295
|
|
|
295
|
-
|
|
296
|
+
export interface IImpressionCountsCacheBase {
|
|
297
|
+
// Used by impressions tracker, in OPTIMIZED and NONE impression modes, to count impressions.
|
|
298
|
+
track(featureName: string, timeFrame: number, amount: number): void
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export interface IUniqueKeysCacheBase {
|
|
302
|
+
// Used by impressions tracker, in NONE impression mode, to track unique keys.
|
|
303
|
+
track(key: string, value: string): void
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Impressions and events cache for standalone and partial consumer modes (sync methods) */
|
|
296
307
|
|
|
297
|
-
//
|
|
298
|
-
export interface
|
|
308
|
+
// API methods for sync recorder storages, used by submitters in standalone mode to pop data and post it to Split BE.
|
|
309
|
+
export interface IRecorderCacheSync<T> {
|
|
299
310
|
// @TODO names are inconsistent with spec
|
|
300
311
|
/* Checks if cache is empty. Returns true if the cache was just created or cleared */
|
|
301
312
|
isEmpty(): boolean
|
|
@@ -305,23 +316,29 @@ export interface IRecorderCacheProducerSync<T> {
|
|
|
305
316
|
pop(toMerge?: T): T
|
|
306
317
|
}
|
|
307
318
|
|
|
308
|
-
|
|
309
|
-
export interface IImpressionsCacheSync extends IImpressionsCacheBase, IRecorderCacheProducerSync<ImpressionDTO[]> {
|
|
319
|
+
export interface IImpressionsCacheSync extends IImpressionsCacheBase, IRecorderCacheSync<ImpressionDTO[]> {
|
|
310
320
|
track(data: ImpressionDTO[]): void
|
|
311
321
|
/* Registers callback for full queue */
|
|
312
322
|
setOnFullQueueCb(cb: () => void): void
|
|
313
323
|
}
|
|
314
324
|
|
|
315
|
-
export interface IEventsCacheSync extends IEventsCacheBase,
|
|
325
|
+
export interface IEventsCacheSync extends IEventsCacheBase, IRecorderCacheSync<SplitIO.EventData[]> {
|
|
316
326
|
track(data: SplitIO.EventData, size?: number): boolean
|
|
317
327
|
/* Registers callback for full queue */
|
|
318
328
|
setOnFullQueueCb(cb: () => void): void
|
|
319
329
|
}
|
|
320
330
|
|
|
321
|
-
|
|
331
|
+
/* Named `ImpressionsCounter` in spec */
|
|
332
|
+
export interface IImpressionCountsCacheSync extends IImpressionCountsCacheBase, IRecorderCacheSync<Record<string, number>> { }
|
|
333
|
+
|
|
334
|
+
export interface IUniqueKeysCacheSync extends IUniqueKeysCacheBase, IRecorderCacheSync<UniqueKeysPayloadSs | UniqueKeysPayloadCs> {
|
|
335
|
+
setOnFullQueueCb(cb: () => void): void,
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Impressions and events cache for consumer and producer modes (async methods) */
|
|
322
339
|
|
|
323
|
-
//
|
|
324
|
-
export interface
|
|
340
|
+
// API methods for async recorder storages, used by submitters in producer mode (synchronizer) to pop data and post it to Split BE.
|
|
341
|
+
export interface IRecorderCacheAsync<T> {
|
|
325
342
|
/* returns the number of stored items */
|
|
326
343
|
count(): Promise<number>
|
|
327
344
|
/* removes the given number of items from the store. If not provided, it deletes all items */
|
|
@@ -330,32 +347,18 @@ export interface IRecorderCacheProducerAsync<T> {
|
|
|
330
347
|
popNWithMetadata(count: number): Promise<T>
|
|
331
348
|
}
|
|
332
349
|
|
|
333
|
-
export interface IImpressionsCacheAsync extends IImpressionsCacheBase,
|
|
350
|
+
export interface IImpressionsCacheAsync extends IImpressionsCacheBase, IRecorderCacheAsync<StoredImpressionWithMetadata[]> {
|
|
334
351
|
// Consumer API method, used by impressions tracker (in standalone and consumer modes) to push data into.
|
|
335
352
|
// The result promise can reject.
|
|
336
353
|
track(data: ImpressionDTO[]): Promise<void>
|
|
337
354
|
}
|
|
338
355
|
|
|
339
|
-
export interface IEventsCacheAsync extends IEventsCacheBase,
|
|
356
|
+
export interface IEventsCacheAsync extends IEventsCacheBase, IRecorderCacheAsync<StoredEventWithMetadata[]> {
|
|
340
357
|
// Consumer API method, used by events tracker (in standalone and consumer modes) to push data into.
|
|
341
358
|
// The result promise cannot reject.
|
|
342
359
|
track(data: SplitIO.EventData, size?: number): Promise<boolean>
|
|
343
360
|
}
|
|
344
361
|
|
|
345
|
-
/**
|
|
346
|
-
* Impression counts cache for impressions dedup in standalone and producer mode.
|
|
347
|
-
* Only in memory. Named `ImpressionsCounter` in spec.
|
|
348
|
-
*/
|
|
349
|
-
export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<Record<string, number>> {
|
|
350
|
-
// Used by impressions tracker
|
|
351
|
-
track(featureName: string, timeFrame: number, amount: number): void
|
|
352
|
-
|
|
353
|
-
// Used by impressions count submitter in standalone and producer mode
|
|
354
|
-
isEmpty(): boolean // check if cache is empty. Return true if the cache was just created or cleared.
|
|
355
|
-
pop(toMerge?: Record<string, number> ): Record<string, number> // pop cache data
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
|
|
359
362
|
/**
|
|
360
363
|
* Telemetry storage interface for standalone and partial consumer modes.
|
|
361
364
|
* Methods are sync because data is stored in memory.
|
|
@@ -415,7 +418,7 @@ export interface ITelemetryEvaluationProducerSync {
|
|
|
415
418
|
|
|
416
419
|
export interface ITelemetryStorageProducerSync extends ITelemetryInitProducerSync, ITelemetryRuntimeProducerSync, ITelemetryEvaluationProducerSync { }
|
|
417
420
|
|
|
418
|
-
export interface ITelemetryCacheSync extends ITelemetryStorageConsumerSync, ITelemetryStorageProducerSync { }
|
|
421
|
+
export interface ITelemetryCacheSync extends ITelemetryStorageConsumerSync, ITelemetryStorageProducerSync, IRecorderCacheSync<TelemetryUsageStatsPayload> { }
|
|
419
422
|
|
|
420
423
|
/**
|
|
421
424
|
* Telemetry storage interface for consumer mode.
|
|
@@ -445,15 +448,18 @@ export interface IStorageBase<
|
|
|
445
448
|
TSplitsCache extends ISplitsCacheBase,
|
|
446
449
|
TSegmentsCache extends ISegmentsCacheBase,
|
|
447
450
|
TImpressionsCache extends IImpressionsCacheBase,
|
|
451
|
+
TImpressionsCountCache extends IImpressionCountsCacheBase,
|
|
448
452
|
TEventsCache extends IEventsCacheBase,
|
|
449
|
-
TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync
|
|
453
|
+
TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync,
|
|
454
|
+
TUniqueKeysCache extends IUniqueKeysCacheBase
|
|
450
455
|
> {
|
|
451
456
|
splits: TSplitsCache,
|
|
452
457
|
segments: TSegmentsCache,
|
|
453
458
|
impressions: TImpressionsCache,
|
|
454
|
-
impressionCounts?:
|
|
459
|
+
impressionCounts?: TImpressionsCountCache,
|
|
455
460
|
events: TEventsCache,
|
|
456
|
-
telemetry?: TTelemetryCache
|
|
461
|
+
telemetry?: TTelemetryCache,
|
|
462
|
+
uniqueKeys?: TUniqueKeysCache,
|
|
457
463
|
destroy(): void | Promise<void>,
|
|
458
464
|
shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this
|
|
459
465
|
}
|
|
@@ -462,16 +468,20 @@ export interface IStorageSync extends IStorageBase<
|
|
|
462
468
|
ISplitsCacheSync,
|
|
463
469
|
ISegmentsCacheSync,
|
|
464
470
|
IImpressionsCacheSync,
|
|
471
|
+
IImpressionCountsCacheSync,
|
|
465
472
|
IEventsCacheSync,
|
|
466
|
-
ITelemetryCacheSync
|
|
473
|
+
ITelemetryCacheSync,
|
|
474
|
+
IUniqueKeysCacheSync
|
|
467
475
|
> { }
|
|
468
476
|
|
|
469
477
|
export interface IStorageAsync extends IStorageBase<
|
|
470
478
|
ISplitsCacheAsync,
|
|
471
479
|
ISegmentsCacheAsync,
|
|
472
480
|
IImpressionsCacheAsync | IImpressionsCacheSync,
|
|
481
|
+
IImpressionCountsCacheBase,
|
|
473
482
|
IEventsCacheAsync | IEventsCacheSync,
|
|
474
|
-
ITelemetryCacheAsync | ITelemetryCacheSync
|
|
483
|
+
ITelemetryCacheAsync | ITelemetryCacheSync,
|
|
484
|
+
IUniqueKeysCacheBase
|
|
475
485
|
> { }
|
|
476
486
|
|
|
477
487
|
/** StorageFactory */
|
|
@@ -479,21 +489,12 @@ export interface IStorageAsync extends IStorageBase<
|
|
|
479
489
|
export type DataLoader = (storage: IStorageSync, matchingKey: string) => void
|
|
480
490
|
|
|
481
491
|
export interface IStorageFactoryParams {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
// ATM, only used by InLocalStorage
|
|
489
|
-
matchingKey?: string, /* undefined on server-side SDKs */
|
|
490
|
-
splitFiltersValidation?: ISplitFiltersValidation,
|
|
491
|
-
|
|
492
|
-
// This callback is invoked when the storage is ready to be used. Error-first callback style: if an error is passed,
|
|
493
|
-
// it means that the storge fail to connect and shouldn't be used.
|
|
494
|
-
// It is meant for emitting SDK_READY event in consumer mode, and for synchronizer to wait before using the storage.
|
|
492
|
+
settings: ISettings,
|
|
493
|
+
/**
|
|
494
|
+
* Error-first callback invoked when the storage is ready to be used. An error means that the storage failed to connect and shouldn't be used.
|
|
495
|
+
* It is meant for emitting SDK_READY event in consumer mode, and waiting before using the storage in the synchronizer.
|
|
496
|
+
*/
|
|
495
497
|
onReadyCb: (error?: any) => void,
|
|
496
|
-
metadata: IMetadata,
|
|
497
498
|
}
|
|
498
499
|
|
|
499
500
|
export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'PLUGGABLE';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Shared utils for Redis and Pluggable storage
|
|
2
|
+
|
|
3
|
+
import { IMetadata } from '../dtos/types';
|
|
4
|
+
import { Method, StoredImpressionWithMetadata } from '../sync/submitters/types';
|
|
5
|
+
import { ImpressionDTO, ISettings } from '../types';
|
|
6
|
+
import { UNKNOWN } from '../utils/constants';
|
|
7
|
+
import { MAX_LATENCY_BUCKET_COUNT } from './inMemory/TelemetryCacheInMemory';
|
|
8
|
+
import { METHOD_NAMES } from './KeyBuilderSS';
|
|
9
|
+
|
|
10
|
+
export function metadataBuilder(settings: Pick<ISettings, 'version' | 'runtime'>): IMetadata {
|
|
11
|
+
return {
|
|
12
|
+
s: settings.version,
|
|
13
|
+
i: settings.runtime.ip || UNKNOWN,
|
|
14
|
+
n: settings.runtime.hostname || UNKNOWN,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Converts impressions to be stored in Redis or pluggable storage.
|
|
19
|
+
export function impressionsToJSON(impressions: ImpressionDTO[], metadata: IMetadata): string[] {
|
|
20
|
+
return impressions.map(impression => {
|
|
21
|
+
const impressionWithMetadata: StoredImpressionWithMetadata = {
|
|
22
|
+
m: metadata,
|
|
23
|
+
i: {
|
|
24
|
+
k: impression.keyName,
|
|
25
|
+
b: impression.bucketingKey,
|
|
26
|
+
f: impression.feature,
|
|
27
|
+
t: impression.treatment,
|
|
28
|
+
r: impression.label,
|
|
29
|
+
c: impression.changeNumber,
|
|
30
|
+
m: impression.time,
|
|
31
|
+
pt: impression.pt,
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return JSON.stringify(impressionWithMetadata);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Utilities used by TelemetryCacheInRedis and TelemetryCachePluggable
|
|
40
|
+
|
|
41
|
+
const REVERSE_METHOD_NAMES = Object.keys(METHOD_NAMES).reduce((acc, key) => {
|
|
42
|
+
acc[METHOD_NAMES[key as Method]] = key as Method;
|
|
43
|
+
return acc;
|
|
44
|
+
}, {} as Record<string, Method>);
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
export function parseMetadata(field: string): [metadata: string] | string {
|
|
48
|
+
const parts = field.split('/');
|
|
49
|
+
if (parts.length !== 3) return `invalid subsection count. Expected 3, got: ${parts.length}`;
|
|
50
|
+
|
|
51
|
+
const [s /* metadata.s */, n /* metadata.n */, i /* metadata.i */] = parts;
|
|
52
|
+
return [JSON.stringify({ s, n, i })];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function parseExceptionField(field: string): [metadata: string, method: Method] | string {
|
|
56
|
+
const parts = field.split('/');
|
|
57
|
+
if (parts.length !== 4) return `invalid subsection count. Expected 4, got: ${parts.length}`;
|
|
58
|
+
|
|
59
|
+
const [s /* metadata.s */, n /* metadata.n */, i /* metadata.i */, m] = parts;
|
|
60
|
+
const method = REVERSE_METHOD_NAMES[m];
|
|
61
|
+
if (!method) return `unknown method '${m}'`;
|
|
62
|
+
|
|
63
|
+
return [JSON.stringify({ s, n, i }), method];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function parseLatencyField(field: string): [metadata: string, method: Method, bucket: number] | string {
|
|
67
|
+
const parts = field.split('/');
|
|
68
|
+
if (parts.length !== 5) return `invalid subsection count. Expected 5, got: ${parts.length}`;
|
|
69
|
+
|
|
70
|
+
const [s /* metadata.s */, n /* metadata.n */, i /* metadata.i */, m, b] = parts;
|
|
71
|
+
const method = REVERSE_METHOD_NAMES[m];
|
|
72
|
+
if (!method) return `unknown method '${m}'`;
|
|
73
|
+
|
|
74
|
+
const bucket = parseInt(b);
|
|
75
|
+
if (isNaN(bucket) || bucket >= MAX_LATENCY_BUCKET_COUNT) return `invalid bucket. Expected a number between 0 and ${MAX_LATENCY_BUCKET_COUNT - 1}, got: ${b}`;
|
|
76
|
+
|
|
77
|
+
return [JSON.stringify({ s, n, i }), method, bucket];
|
|
78
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { syncTaskFactory } from '../syncTask';
|
|
2
2
|
import { ISyncTask } from '../types';
|
|
3
|
-
import {
|
|
3
|
+
import { IRecorderCacheSync } from '../../storages/types';
|
|
4
4
|
import { ILogger } from '../../logger/types';
|
|
5
5
|
import { SUBMITTERS_PUSH, SUBMITTERS_PUSH_FAILS, SUBMITTERS_PUSH_RETRY } from '../../logger/constants';
|
|
6
6
|
import { IResponse } from '../../services/types';
|
|
@@ -11,7 +11,7 @@ import { IResponse } from '../../services/types';
|
|
|
11
11
|
export function submitterFactory<T>(
|
|
12
12
|
log: ILogger,
|
|
13
13
|
postClient: (body: string) => Promise<IResponse>,
|
|
14
|
-
sourceCache:
|
|
14
|
+
sourceCache: IRecorderCacheSync<T>,
|
|
15
15
|
postRate: number,
|
|
16
16
|
dataName: string,
|
|
17
17
|
fromCacheToPayload?: (cacheData: T) => any,
|
|
@@ -4,6 +4,7 @@ import { impressionCountsSubmitterFactory } from './impressionCountsSubmitter';
|
|
|
4
4
|
import { telemetrySubmitterFactory } from './telemetrySubmitter';
|
|
5
5
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
6
6
|
import { ISubmitterManager } from './types';
|
|
7
|
+
import { uniqueKeysSubmitterFactory } from './uniqueKeysSubmitter';
|
|
7
8
|
|
|
8
9
|
export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmitterManager {
|
|
9
10
|
|
|
@@ -15,6 +16,7 @@ export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmit
|
|
|
15
16
|
const impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
|
|
16
17
|
if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter);
|
|
17
18
|
const telemetrySubmitter = telemetrySubmitterFactory(params);
|
|
19
|
+
if (params.storage.uniqueKeys) submitters.push(uniqueKeysSubmitterFactory(params));
|
|
18
20
|
|
|
19
21
|
return {
|
|
20
22
|
// `onlyTelemetry` true if SDK is created with userConsent not GRANTED
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ITelemetryCacheSync } from '../../storages/types';
|
|
2
2
|
import { submitterFactory, firstPushWindowDecorator } from './submitter';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { TelemetryConfigStatsPayload, TelemetryConfigStats } from './types';
|
|
4
|
+
import { CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, NONE, DEBUG_ENUM, OPTIMIZED_ENUM, NONE_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
|
|
5
5
|
import { SDK_READY, SDK_READY_FROM_CACHE } from '../../readiness/constants';
|
|
6
6
|
import { ConsentStatus, ISettings, SDKMode } from '../../types';
|
|
7
7
|
import { base } from '../../utils/settingsValidation';
|
|
@@ -9,41 +9,6 @@ import { usedKeysMap } from '../../utils/inputValidation/apiKey';
|
|
|
9
9
|
import { timer } from '../../utils/timeTracker/timer';
|
|
10
10
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
11
11
|
import { objectAssign } from '../../utils/lang/objectAssign';
|
|
12
|
-
import { isStorageSync } from '../../trackers/impressionObserver/utils';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Converts data from telemetry cache into /metrics/usage request payload.
|
|
16
|
-
*/
|
|
17
|
-
export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits?: ISplitsCacheSync, segments?: ISegmentsCacheSync) {
|
|
18
|
-
return {
|
|
19
|
-
isEmpty() { return false; }, // There is always data in telemetry cache
|
|
20
|
-
clear() { }, // No-op
|
|
21
|
-
|
|
22
|
-
// @TODO consider moving inside telemetry cache for code size reduction
|
|
23
|
-
pop(): TelemetryUsageStatsPayload {
|
|
24
|
-
return {
|
|
25
|
-
lS: telemetry.getLastSynchronization(),
|
|
26
|
-
mL: telemetry.popLatencies(),
|
|
27
|
-
mE: telemetry.popExceptions(),
|
|
28
|
-
hE: telemetry.popHttpErrors(),
|
|
29
|
-
hL: telemetry.popHttpLatencies(),
|
|
30
|
-
tR: telemetry.popTokenRefreshes(),
|
|
31
|
-
aR: telemetry.popAuthRejections(),
|
|
32
|
-
iQ: telemetry.getImpressionStats(QUEUED),
|
|
33
|
-
iDe: telemetry.getImpressionStats(DEDUPED),
|
|
34
|
-
iDr: telemetry.getImpressionStats(DROPPED),
|
|
35
|
-
spC: splits && splits.getSplitNames().length,
|
|
36
|
-
seC: segments && segments.getRegisteredSegments().length,
|
|
37
|
-
skC: segments && segments.getKeysCount(),
|
|
38
|
-
sL: telemetry.getSessionLength(),
|
|
39
|
-
eQ: telemetry.getEventStats(QUEUED),
|
|
40
|
-
eD: telemetry.getEventStats(DROPPED),
|
|
41
|
-
sE: telemetry.popStreamingEvents(),
|
|
42
|
-
t: telemetry.popTags(),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
12
|
|
|
48
13
|
const OPERATION_MODE_MAP = {
|
|
49
14
|
[STANDALONE_MODE]: STANDALONE_ENUM,
|
|
@@ -53,8 +18,9 @@ const OPERATION_MODE_MAP = {
|
|
|
53
18
|
|
|
54
19
|
const IMPRESSIONS_MODE_MAP = {
|
|
55
20
|
[OPTIMIZED]: OPTIMIZED_ENUM,
|
|
56
|
-
[DEBUG]: DEBUG_ENUM
|
|
57
|
-
|
|
21
|
+
[DEBUG]: DEBUG_ENUM,
|
|
22
|
+
[NONE]: NONE_ENUM
|
|
23
|
+
} as Record<ISettings['sync']['impressionsMode'], (0 | 1 | 2)>;
|
|
58
24
|
|
|
59
25
|
const USER_CONSENT_MAP = {
|
|
60
26
|
[CONSENT_UNKNOWN]: 1,
|
|
@@ -130,7 +96,7 @@ export function telemetryCacheConfigAdapter(telemetry: ITelemetryCacheSync, sett
|
|
|
130
96
|
* Submitter that periodically posts telemetry data
|
|
131
97
|
*/
|
|
132
98
|
export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
|
|
133
|
-
const { storage: {
|
|
99
|
+
const { storage: { telemetry }, platform: { now } } = params;
|
|
134
100
|
if (!telemetry || !now) return; // No submitter created if telemetry cache is not defined
|
|
135
101
|
|
|
136
102
|
const { settings, settings: { log, scheduler: { telemetryRefreshRate } }, splitApi, readiness, sdkReadinessManager } = params;
|
|
@@ -139,8 +105,7 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
139
105
|
const submitter = firstPushWindowDecorator(
|
|
140
106
|
submitterFactory(
|
|
141
107
|
log, splitApi.postMetricsUsage,
|
|
142
|
-
|
|
143
|
-
isStorageSync(params.settings) ? telemetryCacheStatsAdapter(telemetry, splits, segments) : telemetryCacheStatsAdapter(telemetry),
|
|
108
|
+
telemetry,
|
|
144
109
|
telemetryRefreshRate, 'telemetry stats', undefined, 0, true
|
|
145
110
|
),
|
|
146
111
|
telemetryRefreshRate
|
|
@@ -37,6 +37,26 @@ export type ImpressionCountsPayload = {
|
|
|
37
37
|
}[]
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
export type UniqueKeysItemSs = {
|
|
41
|
+
/** Split name */
|
|
42
|
+
f: string
|
|
43
|
+
/** keyNames */
|
|
44
|
+
ks: string[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type UniqueKeysPayloadSs = {
|
|
48
|
+
keys: UniqueKeysItemSs[]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type UniqueKeysPayloadCs = {
|
|
52
|
+
keys: {
|
|
53
|
+
/** keyNames */
|
|
54
|
+
k: string
|
|
55
|
+
/** Split name */
|
|
56
|
+
fs: string[]
|
|
57
|
+
}[]
|
|
58
|
+
}
|
|
59
|
+
|
|
40
60
|
export type StoredImpressionWithMetadata = {
|
|
41
61
|
/** Metadata */
|
|
42
62
|
m: IMetadata,
|
|
@@ -56,6 +76,8 @@ export type StoredImpressionWithMetadata = {
|
|
|
56
76
|
c: number,
|
|
57
77
|
/** time */
|
|
58
78
|
m: number
|
|
79
|
+
/** previous time */
|
|
80
|
+
pt?: number
|
|
59
81
|
}
|
|
60
82
|
}
|
|
61
83
|
|
|
@@ -72,27 +94,6 @@ export type MultiMethodExceptions = IMap<string, MethodExceptions>
|
|
|
72
94
|
|
|
73
95
|
export type MultiConfigs = IMap<string, TelemetryConfigStats>
|
|
74
96
|
|
|
75
|
-
// export type StoredLatenciesWithMetadata = {
|
|
76
|
-
// /** Metadata */
|
|
77
|
-
// m: IMetadata,
|
|
78
|
-
// /** Stored latencies */
|
|
79
|
-
// l: MethodLatencies
|
|
80
|
-
// }
|
|
81
|
-
|
|
82
|
-
// export type StoredExceptionsWithMetadata = {
|
|
83
|
-
// /** Metadata */
|
|
84
|
-
// m: IMetadata,
|
|
85
|
-
// /** Stored exceptions */
|
|
86
|
-
// e: MethodExceptions
|
|
87
|
-
// }
|
|
88
|
-
|
|
89
|
-
// export type StoredConfigsWithMetadata = {
|
|
90
|
-
// /** Metadata */
|
|
91
|
-
// m: IMetadata,
|
|
92
|
-
// /** Stored configs */
|
|
93
|
-
// c: TelemetryConfigStats
|
|
94
|
-
// }
|
|
95
|
-
|
|
96
97
|
/**
|
|
97
98
|
* Telemetry usage stats
|
|
98
99
|
*/
|
|
@@ -113,9 +114,9 @@ export type SEGMENT = 'se';
|
|
|
113
114
|
export type MY_SEGMENT = 'ms';
|
|
114
115
|
export type OperationType = SPLITS | IMPRESSIONS | IMPRESSIONS_COUNT | EVENTS | TELEMETRY | TOKEN | SEGMENT | MY_SEGMENT;
|
|
115
116
|
|
|
116
|
-
export type LastSync = Record<OperationType, number | undefined
|
|
117
|
-
export type HttpErrors = Record<OperationType, { [statusCode: string]: number }
|
|
118
|
-
export type HttpLatencies = Record<OperationType, Array<number
|
|
117
|
+
export type LastSync = Partial<Record<OperationType, number | undefined>>
|
|
118
|
+
export type HttpErrors = Partial<Record<OperationType, { [statusCode: string]: number }>>
|
|
119
|
+
export type HttpLatencies = Partial<Record<OperationType, Array<number>>>
|
|
119
120
|
|
|
120
121
|
export type TREATMENT = 't';
|
|
121
122
|
export type TREATMENTS = 'ts';
|
|
@@ -124,9 +125,9 @@ export type TREATMENTS_WITH_CONFIG = 'tcs';
|
|
|
124
125
|
export type TRACK = 'tr';
|
|
125
126
|
export type Method = TREATMENT | TREATMENTS | TREATMENT_WITH_CONFIG | TREATMENTS_WITH_CONFIG | TRACK;
|
|
126
127
|
|
|
127
|
-
export type MethodLatencies = Record<Method, Array<number
|
|
128
|
+
export type MethodLatencies = Partial<Record<Method, Array<number>>>;
|
|
128
129
|
|
|
129
|
-
export type MethodExceptions = Record<Method, number
|
|
130
|
+
export type MethodExceptions = Partial<Record<Method, number>>;
|
|
130
131
|
|
|
131
132
|
export type CONNECTION_ESTABLISHED = 0;
|
|
132
133
|
export type OCCUPANCY_PRI = 10;
|
|
@@ -181,7 +182,8 @@ export type OperationMode = STANDALONE_ENUM | CONSUMER_ENUM | CONSUMER_PARTIAL_E
|
|
|
181
182
|
|
|
182
183
|
export type OPTIMIZED_ENUM = 0;
|
|
183
184
|
export type DEBUG_ENUM = 1;
|
|
184
|
-
export type
|
|
185
|
+
export type NONE_ENUM = 2;
|
|
186
|
+
export type ImpressionsMode = OPTIMIZED_ENUM | DEBUG_ENUM | NONE_ENUM;
|
|
185
187
|
|
|
186
188
|
export type RefreshRates = {
|
|
187
189
|
sp: number, // splits
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
2
|
+
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
3
|
+
import { submitterFactory } from './submitter';
|
|
4
|
+
|
|
5
|
+
const DATA_NAME = 'unique keys';
|
|
6
|
+
const UNIQUE_KEYS_RATE = 900000; // 15 minutes
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Submitter that periodically posts impression counts
|
|
10
|
+
*/
|
|
11
|
+
export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
settings: { log, core: { key } },
|
|
15
|
+
splitApi: { postUniqueKeysBulkCs, postUniqueKeysBulkSs },
|
|
16
|
+
storage: { uniqueKeys }
|
|
17
|
+
} = params;
|
|
18
|
+
|
|
19
|
+
const isClientSide = key !== undefined;
|
|
20
|
+
const postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
|
|
21
|
+
|
|
22
|
+
const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys!, UNIQUE_KEYS_RATE, DATA_NAME);
|
|
23
|
+
|
|
24
|
+
// register unique keys submitter to be executed when uniqueKeys cache is full
|
|
25
|
+
uniqueKeys!.setOnFullQueueCb(() => {
|
|
26
|
+
if (syncTask.isRunning()) {
|
|
27
|
+
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
|
|
28
|
+
syncTask.execute();
|
|
29
|
+
}
|
|
30
|
+
// If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
|
|
31
|
+
// Data will be sent when submitter is resumed.
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return syncTask;
|
|
35
|
+
}
|
|
36
|
+
|