@splitsoftware/splitio-commons 1.6.2-rc.0 → 1.6.2-rc.10
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/listeners/browser.js +2 -1
- package/cjs/logger/constants.js +2 -1
- package/cjs/sdkClient/client.js +19 -7
- package/cjs/sdkClient/sdkClient.js +3 -1
- package/cjs/sdkFactory/index.js +24 -7
- package/cjs/sdkManager/index.js +3 -11
- package/cjs/services/splitApi.js +24 -4
- 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 +73 -0
- package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +78 -0
- package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +50 -0
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +15 -9
- package/cjs/storages/inRedis/TelemetryCacheInRedis.js +100 -0
- package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +59 -0
- package/cjs/storages/inRedis/constants.js +4 -1
- package/cjs/storages/inRedis/index.js +17 -2
- package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +43 -0
- package/cjs/storages/pluggable/SplitsCachePluggable.js +14 -9
- package/cjs/storages/pluggable/TelemetryCachePluggable.js +126 -0
- package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +50 -0
- package/cjs/storages/pluggable/index.js +42 -17
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
- package/cjs/sync/submitters/submitterManager.js +3 -0
- package/cjs/sync/submitters/telemetrySubmitter.js +8 -4
- package/cjs/sync/submitters/uniqueKeysSubmitter.js +27 -0
- 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/telemetryTracker.js +6 -0
- package/cjs/trackers/uniqueKeysTracker.js +38 -0
- package/cjs/utils/constants/index.js +4 -2
- package/cjs/utils/lang/maps.js +15 -7
- package/cjs/utils/redis/RedisMock.js +31 -0
- package/cjs/utils/settingsValidation/impressionsMode.js +2 -2
- package/esm/consent/sdkUserConsent.js +2 -2
- package/esm/evaluator/index.js +15 -16
- package/esm/integrations/ga/GaToSplit.js +8 -5
- package/esm/listeners/browser.js +3 -2
- package/esm/logger/constants.js +1 -0
- package/esm/sdkClient/client.js +19 -7
- package/esm/sdkClient/sdkClient.js +3 -1
- package/esm/sdkFactory/index.js +24 -7
- package/esm/sdkManager/index.js +3 -11
- package/esm/services/splitApi.js +24 -4
- 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 +70 -0
- package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +75 -0
- package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
- package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +47 -0
- package/esm/storages/inRedis/SplitsCacheInRedis.js +15 -9
- package/esm/storages/inRedis/TelemetryCacheInRedis.js +100 -0
- package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +56 -0
- package/esm/storages/inRedis/constants.js +3 -0
- package/esm/storages/inRedis/index.js +18 -3
- package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +40 -0
- package/esm/storages/pluggable/SplitsCachePluggable.js +14 -9
- package/esm/storages/pluggable/TelemetryCachePluggable.js +126 -0
- package/esm/storages/pluggable/UniqueKeysCachePluggable.js +47 -0
- package/esm/storages/pluggable/index.js +43 -18
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
- package/esm/sync/submitters/submitterManager.js +3 -0
- package/esm/sync/submitters/telemetrySubmitter.js +9 -5
- package/esm/sync/submitters/uniqueKeysSubmitter.js +23 -0
- 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/telemetryTracker.js +6 -0
- package/esm/trackers/uniqueKeysTracker.js +34 -0
- package/esm/utils/constants/index.js +2 -0
- package/esm/utils/lang/maps.js +15 -7
- package/esm/utils/redis/RedisMock.js +28 -0
- package/esm/utils/settingsValidation/impressionsMode.js +3 -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/listeners/browser.ts +3 -2
- package/src/logger/constants.ts +1 -0
- package/src/sdkClient/client.ts +21 -8
- package/src/sdkClient/sdkClient.ts +3 -1
- package/src/sdkFactory/index.ts +28 -8
- package/src/sdkFactory/types.ts +7 -4
- package/src/sdkManager/index.ts +3 -12
- package/src/services/splitApi.ts +26 -4
- package/src/services/types.ts +8 -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 +82 -0
- package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +88 -0
- package/src/storages/inRedis/EventsCacheInRedis.ts +1 -1
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +53 -0
- package/src/storages/inRedis/SplitsCacheInRedis.ts +21 -17
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +122 -2
- package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +65 -0
- package/src/storages/inRedis/constants.ts +3 -0
- package/src/storages/inRedis/index.ts +15 -5
- package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +47 -0
- package/src/storages/pluggable/SplitsCachePluggable.ts +20 -17
- package/src/storages/pluggable/TelemetryCachePluggable.ts +147 -2
- package/src/storages/pluggable/UniqueKeysCachePluggable.ts +56 -0
- package/src/storages/pluggable/index.ts +44 -19
- package/src/storages/types.ts +50 -29
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +5 -6
- package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -2
- package/src/sync/submitters/submitterManager.ts +2 -0
- package/src/sync/submitters/telemetrySubmitter.ts +15 -8
- package/src/sync/submitters/types.ts +38 -7
- package/src/sync/submitters/uniqueKeysSubmitter.ts +36 -0
- package/src/trackers/impressionsTracker.ts +27 -48
- 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/telemetryTracker.ts +7 -2
- package/src/trackers/types.ts +32 -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/lang/maps.ts +20 -8
- package/src/utils/redis/RedisMock.ts +33 -0
- package/src/utils/settingsValidation/impressionsMode.ts +3 -3
- package/src/utils/settingsValidation/index.ts +1 -0
- package/types/integrations/types.d.ts +2 -1
- package/types/logger/browser/{debugLogger.d.ts → DebugLogger.d.ts} +0 -0
- package/types/logger/browser/{errorLogger.d.ts → ErrorLogger.d.ts} +0 -0
- package/types/logger/browser/{infoLogger.d.ts → InfoLogger.d.ts} +0 -0
- package/types/logger/browser/{warnLogger.d.ts → WarnLogger.d.ts} +0 -0
- package/types/logger/constants.d.ts +1 -0
- package/types/sdkFactory/types.d.ts +4 -2
- package/types/services/types.d.ts +6 -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 +37 -0
- package/types/storages/inRedis/EventsCacheInRedis.d.ts +1 -1
- package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +14 -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 +15 -0
- package/types/storages/pluggable/ImpressionCountsCachePluggable.d.ts +14 -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 +14 -0
- package/types/storages/types.d.ts +38 -25
- 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 +30 -7
- package/types/sync/submitters/uniqueKeysSubmitter.d.ts +0 -8
- package/types/trackers/impressionsTracker.d.ts +4 -6
- package/types/trackers/types.d.ts +27 -0
- package/types/trackers/uniqueKeysTracker.d.ts +3 -3
- package/types/types.d.ts +1 -1
- package/types/utils/constants/index.d.ts +2 -0
- package/types/utils/lang/maps.d.ts +6 -2
- package/types/utils/redis/RedisMock.d.ts +4 -0
- package/src/splitio.d.ts +0 -1602
- package/types/integrations/ga/GaToSplitPlugin.d.ts +0 -3
- package/types/integrations/ga/SplitToGaPlugin.d.ts +0 -4
- package/types/integrations/ga/autoRequire.d.ts +0 -4
- package/types/logger/codes.d.ts +0 -2
- package/types/logger/codesConstants.d.ts +0 -117
- package/types/logger/codesConstantsBrowser.d.ts +0 -2
- package/types/logger/codesConstantsNode.d.ts +0 -14
- package/types/logger/codesDebug.d.ts +0 -1
- package/types/logger/codesDebugBrowser.d.ts +0 -1
- package/types/logger/codesDebugNode.d.ts +0 -1
- package/types/logger/codesError.d.ts +0 -1
- package/types/logger/codesErrorNode.d.ts +0 -1
- package/types/logger/codesInfo.d.ts +0 -1
- package/types/logger/codesWarn.d.ts +0 -1
- package/types/logger/codesWarnNode.d.ts +0 -1
- package/types/logger/debugLogger.d.ts +0 -2
- package/types/logger/errorLogger.d.ts +0 -2
- package/types/logger/infoLogger.d.ts +0 -2
- package/types/logger/messages/debugBrowser.d.ts +0 -1
- package/types/logger/messages/debugNode.d.ts +0 -1
- package/types/logger/messages/errorNode.d.ts +0 -1
- package/types/logger/messages/warnNode.d.ts +0 -1
- package/types/logger/noopLogger.d.ts +0 -2
- package/types/logger/warnLogger.d.ts +0 -2
- package/types/sdkClient/types.d.ts +0 -18
- package/types/sdkFactory/userConsentProps.d.ts +0 -6
- package/types/sdkManager/sdkManagerMethod.d.ts +0 -6
- package/types/storages/getRegisteredSegments.d.ts +0 -10
- package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
- package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
- package/types/storages/inMemory/index.d.ts +0 -10
- package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
- package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
- package/types/storages/parseSegments.d.ts +0 -6
- package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
- package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
- package/types/sync/polling/syncTasks/splitsSyncTask.copy.d.ts +0 -35
- package/types/sync/polling/syncTasks/splitsSyncTask.morelikeoriginal.d.ts +0 -35
- package/types/sync/streaming/AuthClient/indexV1.d.ts +0 -12
- package/types/sync/streaming/AuthClient/indexV2.d.ts +0 -8
- package/types/sync/streaming/pushManagerCS.d.ts +0 -1
- package/types/sync/streaming/pushManagerNoUsers.d.ts +0 -13
- package/types/sync/streaming/pushManagerSS.d.ts +0 -1
- 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/submitters/telemetrySyncTask.d.ts +0 -0
- package/types/sync/syncManagerFromFile.d.ts +0 -2
- package/types/sync/syncManagerFromObject.d.ts +0 -2
- package/types/sync/syncManagerOffline.d.ts +0 -9
- package/types/sync/syncTaskComposite.d.ts +0 -5
- package/types/trackers/telemetryRecorder.d.ts +0 -0
- package/types/utils/EventEmitter.d.ts +0 -4
- package/types/utils/consent.d.ts +0 -2
- package/types/utils/lang/errors.d.ts +0 -10
- package/types/utils/murmur3/commons.d.ts +0 -12
- package/types/utils/settingsValidation/buildMetadata.d.ts +0 -3
- package/types/utils/settingsValidation/localhost/index.d.ts +0 -9
- package/types/utils/settingsValidation/logger.d.ts +0 -11
- package/types/utils/settingsValidation/runtime/browser.d.ts +0 -2
- package/types/utils/settingsValidation/runtime/node.d.ts +0 -2
- package/types/utils/settingsValidation/userConsent.d.ts +0 -5
- package/types/utils/timeTracker/index.d.ts +0 -1
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { ILogger } from '../../logger/types';
|
|
2
|
-
import { Method } from '../../sync/submitters/types';
|
|
3
|
-
import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
2
|
+
import { Method, MultiConfigs, MultiMethodExceptions, MultiMethodLatencies } from '../../sync/submitters/types';
|
|
3
|
+
import { KeyBuilderSS, parseExceptionField, parseLatencyField, parseMetadata } from '../KeyBuilderSS';
|
|
4
4
|
import { IPluggableStorageWrapper, ITelemetryCacheAsync } from '../types';
|
|
5
5
|
import { findLatencyIndex } from '../findLatencyIndex';
|
|
6
|
+
import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter';
|
|
7
|
+
import { CONSUMER_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
|
|
8
|
+
import { isString, isNaNNumber } from '../../utils/lang';
|
|
9
|
+
import { _Map } from '../../utils/lang/maps';
|
|
10
|
+
import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory';
|
|
6
11
|
|
|
7
12
|
export class TelemetryCachePluggable implements ITelemetryCacheAsync {
|
|
8
13
|
|
|
@@ -23,4 +28,144 @@ export class TelemetryCachePluggable implements ITelemetryCacheAsync {
|
|
|
23
28
|
.catch(() => { /* Handle rejections for telemetry */ });
|
|
24
29
|
}
|
|
25
30
|
|
|
31
|
+
recordConfig() {
|
|
32
|
+
const value = JSON.stringify(getTelemetryConfigStats(CONSUMER_MODE, STORAGE_PLUGGABLE));
|
|
33
|
+
return this.wrapper.set(this.keys.buildInitKey(), value).catch(() => { /* Handle rejections for telemetry */ });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Pop telemetry latencies.
|
|
38
|
+
* The returned promise rejects if wrapper operations fail.
|
|
39
|
+
*/
|
|
40
|
+
popLatencies(): Promise<MultiMethodLatencies> {
|
|
41
|
+
return this.wrapper.getKeysByPrefix(this.keys.latencyPrefix).then(latencyKeys => {
|
|
42
|
+
return latencyKeys.length ?
|
|
43
|
+
this.wrapper.getMany(latencyKeys).then(latencies => {
|
|
44
|
+
|
|
45
|
+
const result: MultiMethodLatencies = new _Map();
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < latencyKeys.length; i++) {
|
|
48
|
+
const field = latencyKeys[i].split('::')[1];
|
|
49
|
+
|
|
50
|
+
const parsedField = parseLatencyField(field);
|
|
51
|
+
if (isString(parsedField)) {
|
|
52
|
+
this.log.error(`Ignoring invalid latency field: ${field}: ${parsedField}`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// @ts-ignore
|
|
57
|
+
const count = parseInt(latencies[i]);
|
|
58
|
+
if (isNaNNumber(count)) {
|
|
59
|
+
this.log.error(`Ignoring latency with invalid count: ${latencies[i]}`);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const [metadata, method, bucket] = parsedField;
|
|
64
|
+
|
|
65
|
+
if (bucket >= MAX_LATENCY_BUCKET_COUNT) {
|
|
66
|
+
this.log.error(`Ignoring latency with invalid bucket: ${bucket}`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!result.has(metadata)) result.set(metadata, {
|
|
71
|
+
t: newBuckets(),
|
|
72
|
+
ts: newBuckets(),
|
|
73
|
+
tc: newBuckets(),
|
|
74
|
+
tcs: newBuckets(),
|
|
75
|
+
tr: newBuckets(),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
result.get(metadata)![method][bucket] = count;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return Promise.all(latencyKeys.map((latencyKey) => this.wrapper.del(latencyKey))).then(() => result);
|
|
82
|
+
}) :
|
|
83
|
+
// If latencyKeys is empty, return an empty map.
|
|
84
|
+
new _Map();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Pop telemetry exceptions.
|
|
90
|
+
* The returned promise rejects if wrapper operations fail.
|
|
91
|
+
*/
|
|
92
|
+
popExceptions(): Promise<MultiMethodExceptions> {
|
|
93
|
+
return this.wrapper.getKeysByPrefix(this.keys.exceptionPrefix).then(exceptionKeys => {
|
|
94
|
+
return exceptionKeys.length ?
|
|
95
|
+
this.wrapper.getMany(exceptionKeys).then(exceptions => {
|
|
96
|
+
|
|
97
|
+
const result: MultiMethodExceptions = new _Map();
|
|
98
|
+
|
|
99
|
+
for (let i = 0; i < exceptionKeys.length; i++) {
|
|
100
|
+
const field = exceptionKeys[i].split('::')[1];
|
|
101
|
+
|
|
102
|
+
const parsedField = parseExceptionField(field);
|
|
103
|
+
if (isString(parsedField)) {
|
|
104
|
+
this.log.error(`Ignoring invalid exception field: ${field}: ${parsedField}`);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// @ts-ignore
|
|
109
|
+
const count = parseInt(exceptions[i]);
|
|
110
|
+
if (isNaNNumber(count)) {
|
|
111
|
+
this.log.error(`Ignoring exception with invalid count: ${exceptions[i]}`);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const [metadata, method] = parsedField;
|
|
116
|
+
|
|
117
|
+
if (!result.has(metadata)) result.set(metadata, {
|
|
118
|
+
t: 0,
|
|
119
|
+
ts: 0,
|
|
120
|
+
tc: 0,
|
|
121
|
+
tcs: 0,
|
|
122
|
+
tr: 0,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
result.get(metadata)![method] = count;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return Promise.all(exceptionKeys.map((exceptionKey) => this.wrapper.del(exceptionKey))).then(() => result);
|
|
129
|
+
}) :
|
|
130
|
+
// If exceptionKeys is empty, return an empty map.
|
|
131
|
+
new _Map();
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Pop telemetry configs.
|
|
137
|
+
* The returned promise rejects if wrapper operations fail.
|
|
138
|
+
*/
|
|
139
|
+
popConfigs(): Promise<MultiConfigs> {
|
|
140
|
+
return this.wrapper.getKeysByPrefix(this.keys.initPrefix).then(configKeys => {
|
|
141
|
+
return configKeys.length ?
|
|
142
|
+
this.wrapper.getMany(configKeys).then(configs => {
|
|
143
|
+
|
|
144
|
+
const result: MultiConfigs = new _Map();
|
|
145
|
+
|
|
146
|
+
for (let i = 0; i < configKeys.length; i++) {
|
|
147
|
+
const field = configKeys[i].split('::')[1];
|
|
148
|
+
|
|
149
|
+
const parsedField = parseMetadata(field);
|
|
150
|
+
if (isString(parsedField)) {
|
|
151
|
+
this.log.error(`Ignoring invalid config field: ${field}: ${parsedField}`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const [metadata] = parsedField;
|
|
156
|
+
|
|
157
|
+
try { // @ts-ignore
|
|
158
|
+
const config = JSON.parse(configs[i]);
|
|
159
|
+
result.set(metadata, config);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
this.log.error(`Ignoring invalid config: ${configs[i]}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return Promise.all(configKeys.map((configKey) => this.wrapper.del(configKey))).then(() => result);
|
|
166
|
+
}) :
|
|
167
|
+
// If configKeys is empty, return an empty map.
|
|
168
|
+
new _Map();
|
|
169
|
+
});
|
|
170
|
+
}
|
|
26
171
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
|
|
8
|
+
export class UniqueKeysCachePluggable extends UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
|
|
9
|
+
|
|
10
|
+
private readonly log: ILogger;
|
|
11
|
+
private readonly key: string;
|
|
12
|
+
private readonly wrapper: IPluggableStorageWrapper;
|
|
13
|
+
private readonly refreshRate: number;
|
|
14
|
+
private intervalId: any;
|
|
15
|
+
|
|
16
|
+
constructor(log: ILogger, key: string, wrapper: IPluggableStorageWrapper, uniqueKeysQueueSize = DEFAULT_CACHE_SIZE, refreshRate = REFRESH_RATE) {
|
|
17
|
+
super(uniqueKeysQueueSize);
|
|
18
|
+
this.log = log;
|
|
19
|
+
this.key = key;
|
|
20
|
+
this.wrapper = wrapper;
|
|
21
|
+
this.refreshRate = refreshRate;
|
|
22
|
+
this.onFullQueue = () => { this.storeUniqueKeys(); };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
storeUniqueKeys() {
|
|
26
|
+
const featureNames = Object.keys(this.uniqueKeysTracker);
|
|
27
|
+
if (!featureNames.length) return Promise.resolve(false);
|
|
28
|
+
|
|
29
|
+
const pipeline = featureNames.reduce<Promise<any>>((pipeline, featureName) => {
|
|
30
|
+
const featureKeys = setToArray(this.uniqueKeysTracker[featureName]);
|
|
31
|
+
const uniqueKeysPayload = {
|
|
32
|
+
f: featureName,
|
|
33
|
+
ks: featureKeys
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return pipeline.then(() => this.wrapper.pushItems(this.key, [JSON.stringify(uniqueKeysPayload)]));
|
|
37
|
+
}, Promise.resolve());
|
|
38
|
+
|
|
39
|
+
this.clear();
|
|
40
|
+
return pipeline.catch(err => {
|
|
41
|
+
this.log.error(`${LOG_PREFIX}Error in uniqueKeys pipeline: ${err}.`);
|
|
42
|
+
return false;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
start() {
|
|
48
|
+
this.intervalId = setInterval(this.storeUniqueKeys.bind(this), this.refreshRate);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
stop() {
|
|
52
|
+
clearInterval(this.intervalId);
|
|
53
|
+
return this.storeUniqueKeys();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
@@ -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,60 @@ 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);
|
|
70
67
|
const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
|
|
68
|
+
const telemetry = shouldRecordTelemetry(params) ?
|
|
69
|
+
isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper) :
|
|
70
|
+
undefined;
|
|
71
|
+
const impressionCountsCache = impressionsMode !== DEBUG ?
|
|
72
|
+
isPartialConsumer ? new ImpressionCountsCacheInMemory() : new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper) :
|
|
73
|
+
undefined;
|
|
74
|
+
const uniqueKeysCache = impressionsMode === NONE ?
|
|
75
|
+
isPartialConsumer ?
|
|
76
|
+
matchingKey === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
|
|
77
|
+
new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
|
|
78
|
+
undefined;
|
|
71
79
|
|
|
72
80
|
// Connects to wrapper and emits SDK_READY event on main client
|
|
73
|
-
|
|
81
|
+
const connectPromise = wrapper.connect().then(() => {
|
|
82
|
+
onReadyCb();
|
|
83
|
+
|
|
84
|
+
// Start periodic flush of async storages
|
|
85
|
+
if (impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
|
|
86
|
+
if (uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
|
|
87
|
+
|
|
88
|
+
// If mode is not defined, it means that the synchronizer is running and so we don't have to record telemetry
|
|
89
|
+
if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig && mode) (telemetry as ITelemetryCacheAsync).recordConfig();
|
|
90
|
+
}).catch((e) => {
|
|
91
|
+
e = e || new Error('Error connecting wrapper');
|
|
92
|
+
onReadyCb(e);
|
|
93
|
+
return e;
|
|
94
|
+
});
|
|
74
95
|
|
|
75
96
|
return {
|
|
76
97
|
splits: new SplitsCachePluggable(log, keys, wrapper),
|
|
77
98
|
segments: new SegmentsCachePluggable(log, keys, wrapper),
|
|
78
99
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
79
|
-
impressionCounts:
|
|
100
|
+
impressionCounts: impressionCountsCache,
|
|
80
101
|
events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
|
|
81
|
-
|
|
82
|
-
|
|
102
|
+
telemetry,
|
|
103
|
+
uniqueKeys: uniqueKeysCache,
|
|
83
104
|
|
|
84
105
|
// Disconnect the underlying storage
|
|
85
106
|
destroy() {
|
|
86
|
-
return
|
|
107
|
+
return Promise.all([
|
|
108
|
+
impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
|
|
109
|
+
uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
|
|
110
|
+
]).then(() => wrapper.disconnect());
|
|
87
111
|
},
|
|
88
112
|
|
|
89
113
|
// emits SDK_READY event on shared clients and returns a reference to the storage
|
|
90
114
|
shared(_, onReadyCb) {
|
|
91
|
-
|
|
115
|
+
connectPromise.then(onReadyCb);
|
|
116
|
+
|
|
92
117
|
return {
|
|
93
118
|
...this,
|
|
94
119
|
// no-op destroy, to disconnect the wrapper only when the main client is destroyed
|
package/src/storages/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
5
|
|
|
6
6
|
/**
|
|
@@ -70,23 +70,25 @@ export interface IPluggableStorageWrapper {
|
|
|
70
70
|
/** Integer operations */
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
* 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.
|
|
74
74
|
*
|
|
75
75
|
* @function incr
|
|
76
76
|
* @param {string} key Key to increment
|
|
77
|
+
* @param {number} increment Value to increment by
|
|
77
78
|
* @returns {Promise<number>} A promise that resolves with the value of key after the increment. The promise rejects if the operation fails,
|
|
78
79
|
* for example, if there is a connection error or the key contains a string that can not be represented as integer.
|
|
79
80
|
*/
|
|
80
|
-
incr: (key: string) => Promise<number>
|
|
81
|
+
incr: (key: string, increment?: number) => Promise<number>
|
|
81
82
|
/**
|
|
82
|
-
* 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.
|
|
83
84
|
*
|
|
84
85
|
* @function decr
|
|
85
86
|
* @param {string} key Key to decrement
|
|
87
|
+
* @param {number} decrement Value to decrement by
|
|
86
88
|
* @returns {Promise<number>} A promise that resolves with the value of key after the decrement. The promise rejects if the operation fails,
|
|
87
89
|
* for example, if there is a connection error or the key contains a string that can not be represented as integer.
|
|
88
90
|
*/
|
|
89
|
-
decr: (key: string) => Promise<number>
|
|
91
|
+
decr: (key: string, decrement?: number) => Promise<number>
|
|
90
92
|
|
|
91
93
|
/** Queue operations */
|
|
92
94
|
|
|
@@ -191,14 +193,14 @@ export interface IPluggableStorageWrapper {
|
|
|
191
193
|
/** Splits cache */
|
|
192
194
|
|
|
193
195
|
export interface ISplitsCacheBase {
|
|
194
|
-
addSplits(entries: [string,
|
|
196
|
+
addSplits(entries: [string, ISplit][]): MaybeThenable<boolean[] | void>,
|
|
195
197
|
removeSplits(names: string[]): MaybeThenable<boolean[] | void>,
|
|
196
|
-
getSplit(name: string): MaybeThenable<
|
|
197
|
-
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
|
|
198
200
|
setChangeNumber(changeNumber: number): MaybeThenable<boolean | void>,
|
|
199
201
|
// should never reject or throw an exception. Instead return -1 by default, assuming no splits are present in the storage.
|
|
200
202
|
getChangeNumber(): MaybeThenable<number>,
|
|
201
|
-
getAll(): MaybeThenable<
|
|
203
|
+
getAll(): MaybeThenable<ISplit[]>,
|
|
202
204
|
getSplitNames(): MaybeThenable<string[]>,
|
|
203
205
|
// should never reject or throw an exception. Instead return true by default, asssuming the TT might exist.
|
|
204
206
|
trafficTypeExists(trafficType: string): MaybeThenable<boolean>,
|
|
@@ -211,13 +213,13 @@ export interface ISplitsCacheBase {
|
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
214
|
-
addSplits(entries: [string,
|
|
216
|
+
addSplits(entries: [string, ISplit][]): boolean[],
|
|
215
217
|
removeSplits(names: string[]): boolean[],
|
|
216
|
-
getSplit(name: string):
|
|
217
|
-
getSplits(names: string[]): Record<string,
|
|
218
|
+
getSplit(name: string): ISplit | null,
|
|
219
|
+
getSplits(names: string[]): Record<string, ISplit | null>,
|
|
218
220
|
setChangeNumber(changeNumber: number): boolean,
|
|
219
221
|
getChangeNumber(): number,
|
|
220
|
-
getAll():
|
|
222
|
+
getAll(): ISplit[],
|
|
221
223
|
getSplitNames(): string[],
|
|
222
224
|
trafficTypeExists(trafficType: string): boolean,
|
|
223
225
|
usesSegments(): boolean,
|
|
@@ -227,13 +229,13 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
230
|
-
addSplits(entries: [string,
|
|
232
|
+
addSplits(entries: [string, ISplit][]): Promise<boolean[] | void>,
|
|
231
233
|
removeSplits(names: string[]): Promise<boolean[] | void>,
|
|
232
|
-
getSplit(name: string): Promise<
|
|
233
|
-
getSplits(names: string[]): Promise<Record<string,
|
|
234
|
+
getSplit(name: string): Promise<ISplit | null>,
|
|
235
|
+
getSplits(names: string[]): Promise<Record<string, ISplit | null>>,
|
|
234
236
|
setChangeNumber(changeNumber: number): Promise<boolean | void>,
|
|
235
237
|
getChangeNumber(): Promise<number>,
|
|
236
|
-
getAll(): Promise<
|
|
238
|
+
getAll(): Promise<ISplit[]>,
|
|
237
239
|
getSplitNames(): Promise<string[]>,
|
|
238
240
|
trafficTypeExists(trafficType: string): Promise<boolean>,
|
|
239
241
|
usesSegments(): Promise<boolean>,
|
|
@@ -347,7 +349,7 @@ export interface IEventsCacheAsync extends IEventsCacheBase, IRecorderCacheProdu
|
|
|
347
349
|
* Only in memory. Named `ImpressionsCounter` in spec.
|
|
348
350
|
*/
|
|
349
351
|
export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<Record<string, number>> {
|
|
350
|
-
|
|
352
|
+
// Used by impressions tracker
|
|
351
353
|
track(featureName: string, timeFrame: number, amount: number): void
|
|
352
354
|
|
|
353
355
|
// Used by impressions count submitter in standalone and producer mode
|
|
@@ -355,6 +357,17 @@ export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<R
|
|
|
355
357
|
pop(toMerge?: Record<string, number> ): Record<string, number> // pop cache data
|
|
356
358
|
}
|
|
357
359
|
|
|
360
|
+
export interface IUniqueKeysCacheBase {
|
|
361
|
+
// Used by unique Keys tracker
|
|
362
|
+
track(key: string, value: string): void
|
|
363
|
+
|
|
364
|
+
// Used by unique keys submitter in standalone and producer mode
|
|
365
|
+
isEmpty(): boolean // check if cache is empty. Return true if the cache was just created or cleared.
|
|
366
|
+
pop(): UniqueKeysPayloadSs | UniqueKeysPayloadCs // pop cache data
|
|
367
|
+
/* Registers callback for full queue */
|
|
368
|
+
setOnFullQueueCb(cb: () => void): void,
|
|
369
|
+
clear(): void
|
|
370
|
+
}
|
|
358
371
|
|
|
359
372
|
/**
|
|
360
373
|
* Telemetry storage interface for standalone and partial consumer modes.
|
|
@@ -423,18 +436,19 @@ export interface ITelemetryCacheSync extends ITelemetryStorageConsumerSync, ITel
|
|
|
423
436
|
*/
|
|
424
437
|
|
|
425
438
|
export interface ITelemetryEvaluationConsumerAsync {
|
|
426
|
-
|
|
427
|
-
|
|
439
|
+
popLatencies(): Promise<MultiMethodLatencies>;
|
|
440
|
+
popExceptions(): Promise<MultiMethodExceptions>;
|
|
441
|
+
popConfigs(): Promise<MultiConfigs>;
|
|
428
442
|
}
|
|
429
443
|
|
|
430
444
|
export interface ITelemetryEvaluationProducerAsync {
|
|
431
445
|
recordLatency(method: Method, latencyMs: number): Promise<any>;
|
|
432
446
|
recordException(method: Method): Promise<any>;
|
|
447
|
+
recordConfig(): Promise<any>;
|
|
433
448
|
}
|
|
434
449
|
|
|
435
450
|
// ATM it only implements the producer API, used by the SDK in consumer mode.
|
|
436
|
-
|
|
437
|
-
export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync { }
|
|
451
|
+
export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync, ITelemetryEvaluationConsumerAsync { }
|
|
438
452
|
|
|
439
453
|
/**
|
|
440
454
|
* Storages
|
|
@@ -444,15 +458,18 @@ export interface IStorageBase<
|
|
|
444
458
|
TSplitsCache extends ISplitsCacheBase,
|
|
445
459
|
TSegmentsCache extends ISegmentsCacheBase,
|
|
446
460
|
TImpressionsCache extends IImpressionsCacheBase,
|
|
461
|
+
TImpressionsCountCache extends IImpressionCountsCacheSync,
|
|
447
462
|
TEventsCache extends IEventsCacheBase,
|
|
448
|
-
TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync
|
|
463
|
+
TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync,
|
|
464
|
+
TUniqueKeysCache extends IUniqueKeysCacheBase
|
|
449
465
|
> {
|
|
450
466
|
splits: TSplitsCache,
|
|
451
467
|
segments: TSegmentsCache,
|
|
452
468
|
impressions: TImpressionsCache,
|
|
453
|
-
impressionCounts?:
|
|
469
|
+
impressionCounts?: TImpressionsCountCache,
|
|
454
470
|
events: TEventsCache,
|
|
455
|
-
telemetry?: TTelemetryCache
|
|
471
|
+
telemetry?: TTelemetryCache,
|
|
472
|
+
uniqueKeys?: TUniqueKeysCache,
|
|
456
473
|
destroy(): void | Promise<void>,
|
|
457
474
|
shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this
|
|
458
475
|
}
|
|
@@ -461,16 +478,20 @@ export interface IStorageSync extends IStorageBase<
|
|
|
461
478
|
ISplitsCacheSync,
|
|
462
479
|
ISegmentsCacheSync,
|
|
463
480
|
IImpressionsCacheSync,
|
|
481
|
+
IImpressionCountsCacheSync,
|
|
464
482
|
IEventsCacheSync,
|
|
465
|
-
ITelemetryCacheSync
|
|
483
|
+
ITelemetryCacheSync,
|
|
484
|
+
IUniqueKeysCacheBase
|
|
466
485
|
> { }
|
|
467
486
|
|
|
468
487
|
export interface IStorageAsync extends IStorageBase<
|
|
469
488
|
ISplitsCacheAsync,
|
|
470
489
|
ISegmentsCacheAsync,
|
|
471
490
|
IImpressionsCacheAsync | IImpressionsCacheSync,
|
|
491
|
+
IImpressionCountsCacheSync,
|
|
472
492
|
IEventsCacheAsync | IEventsCacheSync,
|
|
473
|
-
ITelemetryCacheAsync
|
|
493
|
+
ITelemetryCacheAsync | ITelemetryCacheSync,
|
|
494
|
+
IUniqueKeysCacheBase
|
|
474
495
|
> { }
|
|
475
496
|
|
|
476
497
|
/** StorageFactory */
|
|
@@ -483,7 +504,7 @@ export interface IStorageFactoryParams {
|
|
|
483
504
|
eventsQueueSize?: number,
|
|
484
505
|
optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */,
|
|
485
506
|
mode: SDKMode,
|
|
486
|
-
|
|
507
|
+
impressionsMode?: string,
|
|
487
508
|
// ATM, only used by InLocalStorage
|
|
488
509
|
matchingKey?: string, /* undefined on server-side SDKs */
|
|
489
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);
|
|
@@ -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.uniqueKeysTracker) 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
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
|
|