@splitsoftware/splitio-commons 1.6.2-rc.1 → 1.6.2-rc.11
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 +4 -0
- package/cjs/consent/sdkUserConsent.js +2 -2
- package/cjs/evaluator/index.js +15 -16
- package/cjs/integrations/ga/GaToSplit.js +8 -5
- package/cjs/sdkClient/client.js +19 -7
- package/cjs/sdkClient/sdkClient.js +3 -1
- package/cjs/sdkFactory/index.js +15 -6
- package/cjs/sdkManager/index.js +3 -11
- package/cjs/services/splitApi.js +6 -6
- package/cjs/storages/AbstractSplitsCacheAsync.js +8 -10
- package/cjs/storages/AbstractSplitsCacheSync.js +8 -10
- package/cjs/storages/KeyBuilderSS.js +54 -9
- package/cjs/storages/dataLoader.js +1 -1
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +5 -7
- package/cjs/storages/inLocalStorage/index.js +5 -1
- package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
- package/cjs/storages/inMemory/InMemoryStorage.js +6 -2
- package/cjs/storages/inMemory/InMemoryStorageCS.js +6 -2
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +7 -10
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +10 -5
- package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +72 -0
- package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +76 -0
- package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +85 -0
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +15 -9
- package/cjs/storages/inRedis/TelemetryCacheInRedis.js +100 -0
- package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +71 -0
- package/cjs/storages/inRedis/constants.js +4 -1
- package/cjs/storages/inRedis/index.js +17 -2
- package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +81 -0
- package/cjs/storages/pluggable/SplitsCachePluggable.js +14 -9
- package/cjs/storages/pluggable/TelemetryCachePluggable.js +126 -0
- package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +61 -0
- package/cjs/storages/pluggable/index.js +46 -17
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
- package/cjs/sync/submitters/telemetrySubmitter.js +8 -4
- package/cjs/sync/submitters/uniqueKeysSubmitter.js +16 -59
- package/cjs/trackers/impressionsTracker.js +17 -15
- package/cjs/trackers/strategy/strategyNone.js +1 -1
- package/cjs/trackers/strategy/strategyOptimized.js +2 -1
- package/cjs/trackers/telemetryTracker.js +6 -0
- package/cjs/trackers/uniqueKeysTracker.js +11 -42
- package/cjs/utils/constants/index.js +3 -2
- package/cjs/utils/lang/maps.js +15 -7
- package/cjs/utils/redis/RedisMock.js +31 -0
- package/cjs/utils/settingsValidation/index.js +0 -3
- package/esm/consent/sdkUserConsent.js +2 -2
- package/esm/evaluator/index.js +15 -16
- package/esm/integrations/ga/GaToSplit.js +8 -5
- package/esm/sdkClient/client.js +19 -7
- package/esm/sdkClient/sdkClient.js +3 -1
- package/esm/sdkFactory/index.js +16 -7
- package/esm/sdkManager/index.js +3 -11
- package/esm/services/splitApi.js +6 -6
- package/esm/storages/AbstractSplitsCacheAsync.js +8 -10
- package/esm/storages/AbstractSplitsCacheSync.js +8 -10
- package/esm/storages/KeyBuilderSS.js +50 -8
- package/esm/storages/dataLoader.js +1 -1
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -7
- package/esm/storages/inLocalStorage/index.js +6 -2
- package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
- package/esm/storages/inMemory/InMemoryStorage.js +8 -4
- package/esm/storages/inMemory/InMemoryStorageCS.js +7 -3
- package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -10
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +9 -5
- package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +68 -0
- package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +73 -0
- package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
- package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +82 -0
- package/esm/storages/inRedis/SplitsCacheInRedis.js +15 -9
- package/esm/storages/inRedis/TelemetryCacheInRedis.js +100 -0
- package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +68 -0
- package/esm/storages/inRedis/constants.js +3 -0
- package/esm/storages/inRedis/index.js +18 -3
- package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +78 -0
- package/esm/storages/pluggable/SplitsCachePluggable.js +14 -9
- package/esm/storages/pluggable/TelemetryCachePluggable.js +126 -0
- package/esm/storages/pluggable/UniqueKeysCachePluggable.js +58 -0
- package/esm/storages/pluggable/index.js +47 -18
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
- package/esm/sync/submitters/telemetrySubmitter.js +9 -5
- package/esm/sync/submitters/uniqueKeysSubmitter.js +15 -56
- package/esm/trackers/impressionsTracker.js +17 -15
- package/esm/trackers/strategy/strategyNone.js +1 -1
- package/esm/trackers/strategy/strategyOptimized.js +2 -1
- package/esm/trackers/telemetryTracker.js +6 -0
- package/esm/trackers/uniqueKeysTracker.js +11 -42
- package/esm/utils/constants/index.js +1 -0
- package/esm/utils/lang/maps.js +15 -7
- package/esm/utils/redis/RedisMock.js +28 -0
- package/esm/utils/settingsValidation/index.js +0 -3
- package/package.json +1 -2
- package/src/consent/sdkUserConsent.ts +2 -2
- package/src/evaluator/index.ts +14 -15
- package/src/integrations/ga/GaToSplit.ts +9 -5
- package/src/integrations/types.ts +2 -1
- package/src/logger/.DS_Store +0 -0
- package/src/sdkClient/client.ts +21 -8
- package/src/sdkClient/sdkClient.ts +3 -1
- package/src/sdkFactory/index.ts +17 -7
- package/src/sdkManager/index.ts +3 -12
- package/src/services/splitApi.ts +6 -6
- package/src/services/types.ts +2 -2
- package/src/storages/AbstractSplitsCacheAsync.ts +13 -15
- package/src/storages/AbstractSplitsCacheSync.ts +15 -17
- package/src/storages/KeyBuilderSS.ts +61 -9
- package/src/storages/dataLoader.ts +1 -1
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +8 -11
- package/src/storages/inLocalStorage/index.ts +5 -2
- package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +16 -1
- package/src/storages/inMemory/InMemoryStorage.ts +7 -4
- package/src/storages/inMemory/InMemoryStorageCS.ts +6 -3
- package/src/storages/inMemory/SplitsCacheInMemory.ts +10 -14
- package/src/storages/inMemory/TelemetryCacheInMemory.ts +10 -6
- package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +80 -0
- package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +86 -0
- package/src/storages/inRedis/EventsCacheInRedis.ts +1 -1
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +95 -0
- package/src/storages/inRedis/SplitsCacheInRedis.ts +21 -17
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +122 -2
- package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +77 -0
- package/src/storages/inRedis/constants.ts +3 -0
- package/src/storages/inRedis/index.ts +15 -5
- package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +92 -0
- package/src/storages/pluggable/SplitsCachePluggable.ts +20 -17
- package/src/storages/pluggable/TelemetryCachePluggable.ts +147 -2
- package/src/storages/pluggable/UniqueKeysCachePluggable.ts +67 -0
- package/src/storages/pluggable/index.ts +51 -19
- package/src/storages/types.ts +38 -30
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +5 -6
- package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -2
- package/src/sync/submitters/telemetrySubmitter.ts +15 -8
- package/src/sync/submitters/types.ts +26 -12
- package/src/sync/submitters/uniqueKeysSubmitter.ts +18 -61
- package/src/trackers/impressionsTracker.ts +16 -15
- package/src/trackers/strategy/strategyNone.ts +1 -1
- package/src/trackers/strategy/strategyOptimized.ts +1 -1
- package/src/trackers/telemetryTracker.ts +7 -2
- package/src/trackers/types.ts +9 -7
- package/src/trackers/uniqueKeysTracker.ts +15 -47
- package/src/types.ts +0 -1
- package/src/utils/constants/index.ts +1 -0
- package/src/utils/lang/maps.ts +20 -8
- package/src/utils/redis/RedisMock.ts +33 -0
- package/src/utils/settingsValidation/index.ts +1 -4
- package/types/integrations/types.d.ts +2 -1
- package/types/services/types.d.ts +2 -2
- package/types/storages/AbstractSplitsCacheAsync.d.ts +7 -6
- package/types/storages/AbstractSplitsCacheSync.d.ts +6 -6
- package/types/storages/KeyBuilderSS.d.ts +9 -2
- package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +3 -4
- package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +5 -1
- package/types/storages/inMemory/SplitsCacheInMemory.d.ts +3 -2
- package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +6 -3
- package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +35 -0
- package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +35 -0
- package/types/storages/inRedis/EventsCacheInRedis.d.ts +1 -1
- package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +16 -0
- package/types/storages/inRedis/SplitsCacheInRedis.d.ts +6 -5
- package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +16 -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/SplitsCachePluggable.d.ts +6 -5
- package/types/storages/pluggable/TelemetryCachePluggable.d.ts +17 -1
- package/types/storages/pluggable/UniqueKeysCachePluggable.d.ts +20 -0
- package/types/storages/types.d.ts +35 -35
- package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
- package/types/sync/submitters/telemetrySubmitter.d.ts +1 -1
- package/types/sync/submitters/types.d.ts +19 -12
- package/types/sync/submitters/uniqueKeysSubmitter.d.ts +0 -14
- package/types/trackers/types.d.ts +9 -11
- package/types/trackers/uniqueKeysTracker.d.ts +3 -3
- package/types/types.d.ts +0 -1
- package/types/utils/constants/index.d.ts +1 -0
- package/types/utils/lang/maps.d.ts +6 -2
- package/types/utils/redis/RedisMock.d.ts +4 -0
- package/types/utils/settingsValidation/index.d.ts +0 -1
- package/types/sdkClient/types.d.ts +0 -18
- package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
- package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
- package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
- package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
- package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
- package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
- package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
- package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
- package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
- package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
- package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
- package/types/sync/syncTaskComposite.d.ts +0 -5
- package/types/trackers/filter/bloomFilter.d.ts +0 -10
- package/types/trackers/filter/dictionaryFilter.d.ts +0 -8
- package/types/trackers/filter/types.d.ts +0 -5
- package/types/utils/timeTracker/index.d.ts +0 -70
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { IPluggableStorageWrapper, IUniqueKeysCacheBase } from '../types';
|
|
2
|
+
import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
|
|
3
|
+
import { setToArray } from '../../utils/lang/sets';
|
|
4
|
+
import { DEFAULT_CACHE_SIZE, REFRESH_RATE } from '../inRedis/constants';
|
|
5
|
+
import { LOG_PREFIX } from './constants';
|
|
6
|
+
import { ILogger } from '../../logger/types';
|
|
7
|
+
import { UniqueKeysItemSs } from '../../sync/submitters/types';
|
|
8
|
+
|
|
9
|
+
export class UniqueKeysCachePluggable extends UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
|
|
10
|
+
|
|
11
|
+
private readonly log: ILogger;
|
|
12
|
+
private readonly key: string;
|
|
13
|
+
private readonly wrapper: IPluggableStorageWrapper;
|
|
14
|
+
private readonly refreshRate: number;
|
|
15
|
+
private intervalId: any;
|
|
16
|
+
|
|
17
|
+
constructor(log: ILogger, key: string, wrapper: IPluggableStorageWrapper, uniqueKeysQueueSize = DEFAULT_CACHE_SIZE, refreshRate = REFRESH_RATE) {
|
|
18
|
+
super(uniqueKeysQueueSize);
|
|
19
|
+
this.log = log;
|
|
20
|
+
this.key = key;
|
|
21
|
+
this.wrapper = wrapper;
|
|
22
|
+
this.refreshRate = refreshRate;
|
|
23
|
+
this.onFullQueue = () => { this.storeUniqueKeys(); };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
storeUniqueKeys() {
|
|
27
|
+
const featureNames = Object.keys(this.uniqueKeysTracker);
|
|
28
|
+
if (!featureNames.length) return Promise.resolve(false);
|
|
29
|
+
|
|
30
|
+
const pipeline = featureNames.reduce<Promise<any>>((pipeline, featureName) => {
|
|
31
|
+
const featureKeys = setToArray(this.uniqueKeysTracker[featureName]);
|
|
32
|
+
const uniqueKeysPayload = {
|
|
33
|
+
f: featureName,
|
|
34
|
+
ks: featureKeys
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return pipeline.then(() => this.wrapper.pushItems(this.key, [JSON.stringify(uniqueKeysPayload)]));
|
|
38
|
+
}, Promise.resolve());
|
|
39
|
+
|
|
40
|
+
this.clear();
|
|
41
|
+
return pipeline.catch(err => {
|
|
42
|
+
this.log.error(`${LOG_PREFIX}Error in uniqueKeys pipeline: ${err}.`);
|
|
43
|
+
return false;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
start() {
|
|
49
|
+
this.intervalId = setInterval(this.storeUniqueKeys.bind(this), this.refreshRate);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
stop() {
|
|
53
|
+
clearInterval(this.intervalId);
|
|
54
|
+
return this.storeUniqueKeys();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Async consumer API, used by synchronizer.
|
|
59
|
+
* @param count number of items to pop from the queue. If not provided or equal 0, all items will be popped.
|
|
60
|
+
*/
|
|
61
|
+
popNRaw(count = 0): Promise<UniqueKeysItemSs[]> {
|
|
62
|
+
return Promise.resolve(count || this.wrapper.getItemsCount(this.key))
|
|
63
|
+
.then(count => this.wrapper.popItems(this.key, count))
|
|
64
|
+
.then((uniqueKeyItems) => uniqueKeyItems.map(uniqueKeyItem => JSON.parse(uniqueKeyItem)));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IPluggableStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams } from '../types';
|
|
1
|
+
import { IPluggableStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams, ITelemetryCacheAsync } from '../types';
|
|
2
2
|
|
|
3
3
|
import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
4
4
|
import { SplitsCachePluggable } from './SplitsCachePluggable';
|
|
@@ -8,10 +8,16 @@ 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
|
+
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
|
|
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';
|
|
15
21
|
|
|
16
22
|
const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
|
|
17
23
|
const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
|
|
@@ -35,16 +41,6 @@ function validatePluggableStorageOptions(options: any) {
|
|
|
35
41
|
if (missingMethods.length) throw new Error(`${NO_VALID_WRAPPER_INTERFACE} The following methods are missing or invalid: ${missingMethods}`);
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
// subscription to wrapper connect event in order to emit SDK_READY event
|
|
39
|
-
function wrapperConnect(wrapper: IPluggableStorageWrapper, onReadyCb: (error?: any) => void) {
|
|
40
|
-
wrapper.connect().then(() => {
|
|
41
|
-
onReadyCb();
|
|
42
|
-
// At the moment, we don't synchronize config with pluggable storage
|
|
43
|
-
}).catch((e) => {
|
|
44
|
-
onReadyCb(e || new Error('Error connecting wrapper'));
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
44
|
// Async return type in `client.track` method on consumer partial mode
|
|
49
45
|
// No need to promisify impressions cache
|
|
50
46
|
function promisifyEventsTrack(events: any) {
|
|
@@ -64,31 +60,67 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
64
60
|
|
|
65
61
|
const prefix = validatePrefix(options.prefix);
|
|
66
62
|
|
|
67
|
-
function PluggableStorageFactory(
|
|
63
|
+
function PluggableStorageFactory(params: IStorageFactoryParams): IStorageAsync {
|
|
64
|
+
const { log, metadata, onReadyCb, mode, eventsQueueSize, impressionsQueueSize, impressionsMode, matchingKey } = params;
|
|
68
65
|
const keys = new KeyBuilderSS(prefix, metadata);
|
|
69
66
|
const wrapper = wrapperAdapter(log, options.wrapper);
|
|
67
|
+
|
|
68
|
+
const isSyncronizer = mode === undefined; // If mode is not defined, the synchronizer is running
|
|
70
69
|
const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
|
|
71
70
|
|
|
71
|
+
const telemetry = shouldRecordTelemetry(params) || isSyncronizer ?
|
|
72
|
+
isPartialConsumer ?
|
|
73
|
+
new TelemetryCacheInMemory() :
|
|
74
|
+
new TelemetryCachePluggable(log, keys, wrapper) :
|
|
75
|
+
undefined;
|
|
76
|
+
|
|
77
|
+
const impressionCountsCache = impressionsMode !== DEBUG || isSyncronizer ?
|
|
78
|
+
isPartialConsumer ?
|
|
79
|
+
new ImpressionCountsCacheInMemory() :
|
|
80
|
+
new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper) :
|
|
81
|
+
undefined;
|
|
82
|
+
|
|
83
|
+
const uniqueKeysCache = impressionsMode === NONE || isSyncronizer ?
|
|
84
|
+
isPartialConsumer ?
|
|
85
|
+
matchingKey === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
|
|
86
|
+
new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
|
|
87
|
+
undefined;
|
|
88
|
+
|
|
72
89
|
// Connects to wrapper and emits SDK_READY event on main client
|
|
73
|
-
|
|
90
|
+
const connectPromise = wrapper.connect().then(() => {
|
|
91
|
+
onReadyCb();
|
|
92
|
+
|
|
93
|
+
// Start periodic flush of async storages
|
|
94
|
+
if (impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
|
|
95
|
+
if (uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
|
|
96
|
+
if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig && !isSyncronizer) (telemetry as ITelemetryCacheAsync).recordConfig();
|
|
97
|
+
}).catch((e) => {
|
|
98
|
+
e = e || new Error('Error connecting wrapper');
|
|
99
|
+
onReadyCb(e);
|
|
100
|
+
return e;
|
|
101
|
+
});
|
|
74
102
|
|
|
75
103
|
return {
|
|
76
104
|
splits: new SplitsCachePluggable(log, keys, wrapper),
|
|
77
105
|
segments: new SegmentsCachePluggable(log, keys, wrapper),
|
|
78
106
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
79
|
-
impressionCounts:
|
|
107
|
+
impressionCounts: impressionCountsCache,
|
|
80
108
|
events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
|
|
81
|
-
|
|
82
|
-
|
|
109
|
+
telemetry,
|
|
110
|
+
uniqueKeys: uniqueKeysCache,
|
|
83
111
|
|
|
84
112
|
// Disconnect the underlying storage
|
|
85
113
|
destroy() {
|
|
86
|
-
return
|
|
114
|
+
return Promise.all([
|
|
115
|
+
impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
|
|
116
|
+
uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
|
|
117
|
+
]).then(() => wrapper.disconnect());
|
|
87
118
|
},
|
|
88
119
|
|
|
89
120
|
// emits SDK_READY event on shared clients and returns a reference to the storage
|
|
90
121
|
shared(_, onReadyCb) {
|
|
91
|
-
|
|
122
|
+
connectPromise.then(onReadyCb);
|
|
123
|
+
|
|
92
124
|
return {
|
|
93
125
|
...this,
|
|
94
126
|
// no-op destroy, to disconnect the wrapper only when the main client is destroyed
|
package/src/storages/types.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { MaybeThenable, IMetadata, ISplitFiltersValidation } from '../dtos/types';
|
|
1
|
+
import { MaybeThenable, IMetadata, ISplitFiltersValidation, ISplit } from '../dtos/types';
|
|
2
2
|
import { ILogger } from '../logger/types';
|
|
3
|
-
import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent } from '../sync/submitters/types';
|
|
3
|
+
import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs } from '../sync/submitters/types';
|
|
4
4
|
import { SplitIO, ImpressionDTO, SDKMode } from '../types';
|
|
5
|
-
import { ISet } from '../utils/lang/sets';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Interface of a pluggable storage wrapper.
|
|
@@ -71,23 +70,25 @@ export interface IPluggableStorageWrapper {
|
|
|
71
70
|
/** Integer operations */
|
|
72
71
|
|
|
73
72
|
/**
|
|
74
|
-
* Increments
|
|
73
|
+
* Increments the number stored at `key` by `increment` (or 1 if `increment` is not provided), or set it to `increment` (or 1) if the value doesn't exist.
|
|
75
74
|
*
|
|
76
75
|
* @function incr
|
|
77
76
|
* @param {string} key Key to increment
|
|
77
|
+
* @param {number} increment Value to increment by
|
|
78
78
|
* @returns {Promise<number>} A promise that resolves with the value of key after the increment. The promise rejects if the operation fails,
|
|
79
79
|
* for example, if there is a connection error or the key contains a string that can not be represented as integer.
|
|
80
80
|
*/
|
|
81
|
-
incr: (key: string) => Promise<number>
|
|
81
|
+
incr: (key: string, increment?: number) => Promise<number>
|
|
82
82
|
/**
|
|
83
|
-
* Decrements
|
|
83
|
+
* Decrements the number stored at `key` by `decrement` (or 1 if `decrement` is not provided), or set it to minus `decrement` (or minus 1) if the value doesn't exist.
|
|
84
84
|
*
|
|
85
85
|
* @function decr
|
|
86
86
|
* @param {string} key Key to decrement
|
|
87
|
+
* @param {number} decrement Value to decrement by
|
|
87
88
|
* @returns {Promise<number>} A promise that resolves with the value of key after the decrement. The promise rejects if the operation fails,
|
|
88
89
|
* for example, if there is a connection error or the key contains a string that can not be represented as integer.
|
|
89
90
|
*/
|
|
90
|
-
decr: (key: string) => Promise<number>
|
|
91
|
+
decr: (key: string, decrement?: number) => Promise<number>
|
|
91
92
|
|
|
92
93
|
/** Queue operations */
|
|
93
94
|
|
|
@@ -192,14 +193,14 @@ export interface IPluggableStorageWrapper {
|
|
|
192
193
|
/** Splits cache */
|
|
193
194
|
|
|
194
195
|
export interface ISplitsCacheBase {
|
|
195
|
-
addSplits(entries: [string,
|
|
196
|
+
addSplits(entries: [string, ISplit][]): MaybeThenable<boolean[] | void>,
|
|
196
197
|
removeSplits(names: string[]): MaybeThenable<boolean[] | void>,
|
|
197
|
-
getSplit(name: string): MaybeThenable<
|
|
198
|
-
getSplits(names: string[]): MaybeThenable<Record<string,
|
|
198
|
+
getSplit(name: string): MaybeThenable<ISplit | null>,
|
|
199
|
+
getSplits(names: string[]): MaybeThenable<Record<string, ISplit | null>>, // `fetchMany` in spec
|
|
199
200
|
setChangeNumber(changeNumber: number): MaybeThenable<boolean | void>,
|
|
200
201
|
// should never reject or throw an exception. Instead return -1 by default, assuming no splits are present in the storage.
|
|
201
202
|
getChangeNumber(): MaybeThenable<number>,
|
|
202
|
-
getAll(): MaybeThenable<
|
|
203
|
+
getAll(): MaybeThenable<ISplit[]>,
|
|
203
204
|
getSplitNames(): MaybeThenable<string[]>,
|
|
204
205
|
// should never reject or throw an exception. Instead return true by default, asssuming the TT might exist.
|
|
205
206
|
trafficTypeExists(trafficType: string): MaybeThenable<boolean>,
|
|
@@ -212,13 +213,13 @@ export interface ISplitsCacheBase {
|
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
215
|
-
addSplits(entries: [string,
|
|
216
|
+
addSplits(entries: [string, ISplit][]): boolean[],
|
|
216
217
|
removeSplits(names: string[]): boolean[],
|
|
217
|
-
getSplit(name: string):
|
|
218
|
-
getSplits(names: string[]): Record<string,
|
|
218
|
+
getSplit(name: string): ISplit | null,
|
|
219
|
+
getSplits(names: string[]): Record<string, ISplit | null>,
|
|
219
220
|
setChangeNumber(changeNumber: number): boolean,
|
|
220
221
|
getChangeNumber(): number,
|
|
221
|
-
getAll():
|
|
222
|
+
getAll(): ISplit[],
|
|
222
223
|
getSplitNames(): string[],
|
|
223
224
|
trafficTypeExists(trafficType: string): boolean,
|
|
224
225
|
usesSegments(): boolean,
|
|
@@ -228,13 +229,13 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
228
229
|
}
|
|
229
230
|
|
|
230
231
|
export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
231
|
-
addSplits(entries: [string,
|
|
232
|
+
addSplits(entries: [string, ISplit][]): Promise<boolean[] | void>,
|
|
232
233
|
removeSplits(names: string[]): Promise<boolean[] | void>,
|
|
233
|
-
getSplit(name: string): Promise<
|
|
234
|
-
getSplits(names: string[]): Promise<Record<string,
|
|
234
|
+
getSplit(name: string): Promise<ISplit | null>,
|
|
235
|
+
getSplits(names: string[]): Promise<Record<string, ISplit | null>>,
|
|
235
236
|
setChangeNumber(changeNumber: number): Promise<boolean | void>,
|
|
236
237
|
getChangeNumber(): Promise<number>,
|
|
237
|
-
getAll(): Promise<
|
|
238
|
+
getAll(): Promise<ISplit[]>,
|
|
238
239
|
getSplitNames(): Promise<string[]>,
|
|
239
240
|
trafficTypeExists(trafficType: string): Promise<boolean>,
|
|
240
241
|
usesSegments(): Promise<boolean>,
|
|
@@ -353,16 +354,19 @@ export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<R
|
|
|
353
354
|
|
|
354
355
|
// Used by impressions count submitter in standalone and producer mode
|
|
355
356
|
isEmpty(): boolean // check if cache is empty. Return true if the cache was just created or cleared.
|
|
356
|
-
pop(toMerge?: Record<string, number>
|
|
357
|
+
pop(toMerge?: Record<string, number>): Record<string, number> // pop cache data
|
|
357
358
|
}
|
|
358
359
|
|
|
359
|
-
export interface IUniqueKeysCacheBase
|
|
360
|
+
export interface IUniqueKeysCacheBase {
|
|
360
361
|
// Used by unique Keys tracker
|
|
361
|
-
track(
|
|
362
|
+
track(key: string, value: string): void
|
|
362
363
|
|
|
363
364
|
// Used by unique keys submitter in standalone and producer mode
|
|
364
365
|
isEmpty(): boolean // check if cache is empty. Return true if the cache was just created or cleared.
|
|
365
|
-
pop(
|
|
366
|
+
pop(): UniqueKeysPayloadSs | UniqueKeysPayloadCs // pop cache data
|
|
367
|
+
/* Registers callback for full queue */
|
|
368
|
+
setOnFullQueueCb(cb: () => void): void,
|
|
369
|
+
clear(): void
|
|
366
370
|
}
|
|
367
371
|
|
|
368
372
|
/**
|
|
@@ -432,18 +436,19 @@ export interface ITelemetryCacheSync extends ITelemetryStorageConsumerSync, ITel
|
|
|
432
436
|
*/
|
|
433
437
|
|
|
434
438
|
export interface ITelemetryEvaluationConsumerAsync {
|
|
435
|
-
|
|
436
|
-
|
|
439
|
+
popLatencies(): Promise<MultiMethodLatencies>;
|
|
440
|
+
popExceptions(): Promise<MultiMethodExceptions>;
|
|
441
|
+
popConfigs(): Promise<MultiConfigs>;
|
|
437
442
|
}
|
|
438
443
|
|
|
439
444
|
export interface ITelemetryEvaluationProducerAsync {
|
|
440
445
|
recordLatency(method: Method, latencyMs: number): Promise<any>;
|
|
441
446
|
recordException(method: Method): Promise<any>;
|
|
447
|
+
recordConfig(): Promise<any>;
|
|
442
448
|
}
|
|
443
449
|
|
|
444
450
|
// ATM it only implements the producer API, used by the SDK in consumer mode.
|
|
445
|
-
|
|
446
|
-
export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync { }
|
|
451
|
+
export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync, ITelemetryEvaluationConsumerAsync { }
|
|
447
452
|
|
|
448
453
|
/**
|
|
449
454
|
* Storages
|
|
@@ -453,6 +458,7 @@ export interface IStorageBase<
|
|
|
453
458
|
TSplitsCache extends ISplitsCacheBase,
|
|
454
459
|
TSegmentsCache extends ISegmentsCacheBase,
|
|
455
460
|
TImpressionsCache extends IImpressionsCacheBase,
|
|
461
|
+
TImpressionsCountCache extends IImpressionCountsCacheSync,
|
|
456
462
|
TEventsCache extends IEventsCacheBase,
|
|
457
463
|
TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync,
|
|
458
464
|
TUniqueKeysCache extends IUniqueKeysCacheBase
|
|
@@ -460,7 +466,7 @@ export interface IStorageBase<
|
|
|
460
466
|
splits: TSplitsCache,
|
|
461
467
|
segments: TSegmentsCache,
|
|
462
468
|
impressions: TImpressionsCache,
|
|
463
|
-
impressionCounts?:
|
|
469
|
+
impressionCounts?: TImpressionsCountCache,
|
|
464
470
|
events: TEventsCache,
|
|
465
471
|
telemetry?: TTelemetryCache,
|
|
466
472
|
uniqueKeys?: TUniqueKeysCache,
|
|
@@ -472,6 +478,7 @@ export interface IStorageSync extends IStorageBase<
|
|
|
472
478
|
ISplitsCacheSync,
|
|
473
479
|
ISegmentsCacheSync,
|
|
474
480
|
IImpressionsCacheSync,
|
|
481
|
+
IImpressionCountsCacheSync,
|
|
475
482
|
IEventsCacheSync,
|
|
476
483
|
ITelemetryCacheSync,
|
|
477
484
|
IUniqueKeysCacheBase
|
|
@@ -481,8 +488,9 @@ export interface IStorageAsync extends IStorageBase<
|
|
|
481
488
|
ISplitsCacheAsync,
|
|
482
489
|
ISegmentsCacheAsync,
|
|
483
490
|
IImpressionsCacheAsync | IImpressionsCacheSync,
|
|
491
|
+
IImpressionCountsCacheSync,
|
|
484
492
|
IEventsCacheAsync | IEventsCacheSync,
|
|
485
|
-
ITelemetryCacheAsync,
|
|
493
|
+
ITelemetryCacheAsync | ITelemetryCacheSync,
|
|
486
494
|
IUniqueKeysCacheBase
|
|
487
495
|
> { }
|
|
488
496
|
|
|
@@ -496,7 +504,7 @@ export interface IStorageFactoryParams {
|
|
|
496
504
|
eventsQueueSize?: number,
|
|
497
505
|
optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */,
|
|
498
506
|
mode: SDKMode,
|
|
499
|
-
|
|
507
|
+
impressionsMode?: string,
|
|
500
508
|
// ATM, only used by InLocalStorage
|
|
501
509
|
matchingKey?: string, /* undefined on server-side SDKs */
|
|
502
510
|
splitFiltersValidation?: ISplitFiltersValidation,
|
|
@@ -2,7 +2,7 @@ import { forOwn } from '../../../utils/lang';
|
|
|
2
2
|
import { IReadinessManager } from '../../../readiness/types';
|
|
3
3
|
import { ISplitsCacheSync } from '../../../storages/types';
|
|
4
4
|
import { ISplitsParser } from '../splitsParser/types';
|
|
5
|
-
import { ISplitPartial } from '../../../dtos/types';
|
|
5
|
+
import { ISplit, ISplitPartial } from '../../../dtos/types';
|
|
6
6
|
import { syncTaskFactory } from '../../syncTask';
|
|
7
7
|
import { ISyncTask } from '../../types';
|
|
8
8
|
import { ISettings } from '../../../types';
|
|
@@ -24,7 +24,7 @@ export function fromObjectUpdaterFactory(
|
|
|
24
24
|
let startingUp = true;
|
|
25
25
|
|
|
26
26
|
return function objectUpdater() {
|
|
27
|
-
const splits: [string,
|
|
27
|
+
const splits: [string, ISplit][] = [];
|
|
28
28
|
let loadError = null;
|
|
29
29
|
let splitsMock: false | Record<string, ISplitPartial> = {};
|
|
30
30
|
try {
|
|
@@ -38,9 +38,8 @@ export function fromObjectUpdaterFactory(
|
|
|
38
38
|
log.debug(SYNC_OFFLINE_DATA, [JSON.stringify(splitsMock)]);
|
|
39
39
|
|
|
40
40
|
forOwn(splitsMock, function (val, name) {
|
|
41
|
-
splits.push([
|
|
42
|
-
name,
|
|
43
|
-
JSON.stringify({
|
|
41
|
+
splits.push([ // @ts-ignore Split changeNumber and seed is undefined in localhost mode
|
|
42
|
+
name, {
|
|
44
43
|
name,
|
|
45
44
|
status: 'ACTIVE',
|
|
46
45
|
killed: false,
|
|
@@ -49,7 +48,7 @@ export function fromObjectUpdaterFactory(
|
|
|
49
48
|
conditions: val.conditions || [],
|
|
50
49
|
configurations: val.configurations,
|
|
51
50
|
trafficTypeName: val.trafficTypeName
|
|
52
|
-
}
|
|
51
|
+
}
|
|
53
52
|
]);
|
|
54
53
|
});
|
|
55
54
|
|
|
@@ -40,7 +40,7 @@ export function parseSegments({ conditions }: ISplit): ISet<string> {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
interface ISplitMutations {
|
|
43
|
-
added: [string,
|
|
43
|
+
added: [string, ISplit][],
|
|
44
44
|
removed: string[],
|
|
45
45
|
segments: string[]
|
|
46
46
|
}
|
|
@@ -54,7 +54,7 @@ export function computeSplitsMutation(entries: ISplit[]): ISplitMutations {
|
|
|
54
54
|
const segments = new _Set<string>();
|
|
55
55
|
const computed = entries.reduce((accum, split) => {
|
|
56
56
|
if (split.status === 'ACTIVE') {
|
|
57
|
-
accum.added.push([split.name,
|
|
57
|
+
accum.added.push([split.name, split]);
|
|
58
58
|
|
|
59
59
|
parseSegments(split).forEach((segmentName: string) => {
|
|
60
60
|
segments.add(segmentName);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ISegmentsCacheSync, ISplitsCacheSync, ITelemetryCacheSync } from '../../storages/types';
|
|
2
2
|
import { submitterFactory, firstPushWindowDecorator } from './submitter';
|
|
3
3
|
import { TelemetryUsageStatsPayload, TelemetryConfigStatsPayload, TelemetryConfigStats } from './types';
|
|
4
|
-
import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, DEBUG_ENUM, OPTIMIZED_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
|
|
4
|
+
import { QUEUED, DEDUPED, DROPPED, 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,11 +9,12 @@ 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';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Converts data from telemetry cache into /metrics/usage request payload.
|
|
15
16
|
*/
|
|
16
|
-
export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits
|
|
17
|
+
export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits?: ISplitsCacheSync, segments?: ISegmentsCacheSync) {
|
|
17
18
|
return {
|
|
18
19
|
isEmpty() { return false; }, // There is always data in telemetry cache
|
|
19
20
|
clear() { }, // No-op
|
|
@@ -31,9 +32,9 @@ export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, split
|
|
|
31
32
|
iQ: telemetry.getImpressionStats(QUEUED),
|
|
32
33
|
iDe: telemetry.getImpressionStats(DEDUPED),
|
|
33
34
|
iDr: telemetry.getImpressionStats(DROPPED),
|
|
34
|
-
spC: splits.getSplitNames().length,
|
|
35
|
-
seC: segments.getRegisteredSegments().length,
|
|
36
|
-
skC: segments.getKeysCount(),
|
|
35
|
+
spC: splits && splits.getSplitNames().length,
|
|
36
|
+
seC: segments && segments.getRegisteredSegments().length,
|
|
37
|
+
skC: segments && segments.getKeysCount(),
|
|
37
38
|
sL: telemetry.getSessionLength(),
|
|
38
39
|
eQ: telemetry.getEventStats(QUEUED),
|
|
39
40
|
eD: telemetry.getEventStats(DROPPED),
|
|
@@ -52,8 +53,9 @@ const OPERATION_MODE_MAP = {
|
|
|
52
53
|
|
|
53
54
|
const IMPRESSIONS_MODE_MAP = {
|
|
54
55
|
[OPTIMIZED]: OPTIMIZED_ENUM,
|
|
55
|
-
[DEBUG]: DEBUG_ENUM
|
|
56
|
-
|
|
56
|
+
[DEBUG]: DEBUG_ENUM,
|
|
57
|
+
[NONE]: NONE_ENUM
|
|
58
|
+
} as Record<ISettings['sync']['impressionsMode'], (0 | 1 | 2)>;
|
|
57
59
|
|
|
58
60
|
const USER_CONSENT_MAP = {
|
|
59
61
|
[CONSENT_UNKNOWN]: 1,
|
|
@@ -136,7 +138,12 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
136
138
|
const startTime = timer(now);
|
|
137
139
|
|
|
138
140
|
const submitter = firstPushWindowDecorator(
|
|
139
|
-
submitterFactory(
|
|
141
|
+
submitterFactory(
|
|
142
|
+
log, splitApi.postMetricsUsage,
|
|
143
|
+
// @TODO cannot provide splits and segments cache if they are async, because `submitterFactory` expects a sync storage source
|
|
144
|
+
isStorageSync(params.settings) ? telemetryCacheStatsAdapter(telemetry, splits, segments) : telemetryCacheStatsAdapter(telemetry),
|
|
145
|
+
telemetryRefreshRate, 'telemetry stats', undefined, 0, true
|
|
146
|
+
),
|
|
140
147
|
telemetryRefreshRate
|
|
141
148
|
);
|
|
142
149
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
1
2
|
import { IMetadata } from '../../dtos/types';
|
|
2
3
|
import { SplitIO } from '../../types';
|
|
4
|
+
import { IMap } from '../../utils/lang/maps';
|
|
3
5
|
import { ISyncTask } from '../types';
|
|
4
6
|
|
|
5
7
|
export type ImpressionsPayload = {
|
|
@@ -35,13 +37,15 @@ export type ImpressionCountsPayload = {
|
|
|
35
37
|
}[]
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
export type UniqueKeysItemSs = {
|
|
41
|
+
/** Split name */
|
|
42
|
+
f: string
|
|
43
|
+
/** keyNames */
|
|
44
|
+
ks: string[]
|
|
45
|
+
}
|
|
46
|
+
|
|
38
47
|
export type UniqueKeysPayloadSs = {
|
|
39
|
-
keys:
|
|
40
|
-
/** Split name */
|
|
41
|
-
f: string
|
|
42
|
-
/** keyNames */
|
|
43
|
-
ks: string[]
|
|
44
|
-
}[]
|
|
48
|
+
keys: UniqueKeysItemSs[]
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
export type UniqueKeysPayloadCs = {
|
|
@@ -82,6 +86,12 @@ export type StoredEventWithMetadata = {
|
|
|
82
86
|
e: SplitIO.EventData
|
|
83
87
|
}
|
|
84
88
|
|
|
89
|
+
export type MultiMethodLatencies = IMap<string, MethodLatencies>
|
|
90
|
+
|
|
91
|
+
export type MultiMethodExceptions = IMap<string, MethodExceptions>
|
|
92
|
+
|
|
93
|
+
export type MultiConfigs = IMap<string, TelemetryConfigStats>
|
|
94
|
+
|
|
85
95
|
/**
|
|
86
96
|
* Telemetry usage stats
|
|
87
97
|
*/
|
|
@@ -133,11 +143,15 @@ export type StreamingEvent = {
|
|
|
133
143
|
t: number, // timestamp
|
|
134
144
|
}
|
|
135
145
|
|
|
146
|
+
// 'telemetry.latencias' and 'telemetry.exceptions' Redis/Pluggable keys
|
|
147
|
+
export type TelemetryUsageStats = {
|
|
148
|
+
mL?: MethodLatencies, // clientMethodLatencies
|
|
149
|
+
mE?: MethodExceptions, // methodExceptions
|
|
150
|
+
}
|
|
151
|
+
|
|
136
152
|
// 'metrics/usage' JSON request body
|
|
137
|
-
export type TelemetryUsageStatsPayload = {
|
|
153
|
+
export type TelemetryUsageStatsPayload = TelemetryUsageStats & {
|
|
138
154
|
lS: LastSync, // lastSynchronization
|
|
139
|
-
mL: MethodLatencies, // clientMethodLatencies
|
|
140
|
-
mE: MethodExceptions, // methodExceptions
|
|
141
155
|
hE: HttpErrors, // httpErrors
|
|
142
156
|
hL: HttpLatencies, // httpLatencies
|
|
143
157
|
tR: number, // tokenRefreshes
|
|
@@ -145,9 +159,9 @@ export type TelemetryUsageStatsPayload = {
|
|
|
145
159
|
iQ: number, // impressionsQueued
|
|
146
160
|
iDe: number, // impressionsDeduped
|
|
147
161
|
iDr: number, // impressionsDropped
|
|
148
|
-
spC
|
|
149
|
-
seC
|
|
150
|
-
skC
|
|
162
|
+
spC?: number, // splitCount
|
|
163
|
+
seC?: number, // segmentCount
|
|
164
|
+
skC?: number, // segmentKeyCount
|
|
151
165
|
sL?: number, // sessionLengthMs
|
|
152
166
|
eQ: number, // eventsQueued
|
|
153
167
|
eD: number, // eventsDropped
|
|
@@ -1,63 +1,9 @@
|
|
|
1
|
+
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
1
2
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
2
|
-
import { ISet, setToArray } from '../../utils/lang/sets';
|
|
3
3
|
import { submitterFactory } from './submitter';
|
|
4
|
-
import { UniqueKeysPayloadCs, UniqueKeysPayloadSs } from './types';
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*/
|
|
9
|
-
function invertUniqueKeys(uniqueKeys: { [featureName: string]: ISet<string> }): { [key: string]: string[] } {
|
|
10
|
-
const featureNames = Object.keys(uniqueKeys);
|
|
11
|
-
const inverted: { [key: string]: string[] } = {};
|
|
12
|
-
for (let i = 0; i < featureNames.length; i++) {
|
|
13
|
-
const featureName = featureNames[i];
|
|
14
|
-
const featureKeys = setToArray(uniqueKeys[featureName]);
|
|
15
|
-
for (let j = 0; j< featureKeys.length; j++) {
|
|
16
|
-
const featureKey = featureKeys[j];
|
|
17
|
-
if (!inverted[featureKey]) inverted[featureKey] = [];
|
|
18
|
-
inverted[featureKey].push(featureName);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return inverted;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Converts `uniqueKeys` data from cache into request payload for CS.
|
|
26
|
-
*/
|
|
27
|
-
export function fromUniqueKeysCollectorCs(uniqueKeys: { [featureName: string]: ISet<string> }): UniqueKeysPayloadCs {
|
|
28
|
-
const payload = [];
|
|
29
|
-
const featuresPerKey = invertUniqueKeys(uniqueKeys);
|
|
30
|
-
const keys = Object.keys(featuresPerKey);
|
|
31
|
-
for (let k = 0; k < keys.length; k++) {
|
|
32
|
-
const key = keys[k];
|
|
33
|
-
const uniqueKeysPayload = {
|
|
34
|
-
k: key,
|
|
35
|
-
fs: featuresPerKey[key]
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
payload.push(uniqueKeysPayload);
|
|
39
|
-
}
|
|
40
|
-
return { keys: payload };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Converts `uniqueKeys` data from cache into request payload for SS.
|
|
45
|
-
*/
|
|
46
|
-
export function fromUniqueKeysCollectorSs(uniqueKeys: { [featureName: string]: ISet<string> }): UniqueKeysPayloadSs {
|
|
47
|
-
const payload = [];
|
|
48
|
-
const featureNames = Object.keys(uniqueKeys);
|
|
49
|
-
for (let i = 0; i < featureNames.length; i++) {
|
|
50
|
-
const featureName = featureNames[i];
|
|
51
|
-
const featureKeys = setToArray(uniqueKeys[featureName]);
|
|
52
|
-
const uniqueKeysPayload = {
|
|
53
|
-
f: featureName,
|
|
54
|
-
ks: featureKeys
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
payload.push(uniqueKeysPayload);
|
|
58
|
-
}
|
|
59
|
-
return { keys: payload };
|
|
60
|
-
}
|
|
5
|
+
const DATA_NAME = 'unique keys';
|
|
6
|
+
const UNIQUE_KEYS_RATE = 900000; // 15 minutes
|
|
61
7
|
|
|
62
8
|
/**
|
|
63
9
|
* Submitter that periodically posts impression counts
|
|
@@ -65,15 +11,26 @@ export function fromUniqueKeysCollectorSs(uniqueKeys: { [featureName: string]: I
|
|
|
65
11
|
export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
66
12
|
|
|
67
13
|
const {
|
|
68
|
-
settings: { log,
|
|
14
|
+
settings: { log, core: { key } },
|
|
69
15
|
splitApi: { postUniqueKeysBulkCs, postUniqueKeysBulkSs },
|
|
70
16
|
storage: { uniqueKeys }
|
|
71
17
|
} = params;
|
|
72
|
-
|
|
18
|
+
|
|
73
19
|
const isClientSide = key !== undefined;
|
|
74
20
|
const postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
|
|
75
|
-
const fromUniqueKeysCollector = isClientSide ? fromUniqueKeysCollectorCs : fromUniqueKeysCollectorSs;
|
|
76
21
|
|
|
77
|
-
|
|
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;
|
|
78
35
|
}
|
|
79
36
|
|