@splitsoftware/splitio-commons 2.1.0-rc.1 → 2.1.0-rc.2
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 +5 -1
- package/LICENSE +1 -1
- package/cjs/evaluator/index.js +2 -0
- package/cjs/listeners/browser.js +4 -6
- package/cjs/sdkClient/client.js +14 -11
- package/cjs/sdkClient/sdkClient.js +1 -1
- package/cjs/sdkFactory/index.js +9 -14
- package/cjs/sdkManager/index.js +2 -1
- package/cjs/storages/inLocalStorage/index.js +4 -3
- package/cjs/storages/inMemory/InMemoryStorage.js +5 -7
- package/cjs/storages/inMemory/InMemoryStorageCS.js +6 -7
- package/cjs/storages/inRedis/index.js +9 -13
- package/cjs/storages/pluggable/index.js +15 -19
- package/cjs/sync/submitters/impressionCountsSubmitter.js +2 -4
- package/cjs/sync/submitters/submitterManager.js +3 -6
- package/cjs/trackers/impressionsTracker.js +17 -18
- package/cjs/trackers/strategy/strategyDebug.js +4 -11
- package/cjs/trackers/strategy/strategyNone.js +11 -16
- package/cjs/trackers/strategy/strategyOptimized.js +11 -21
- package/esm/evaluator/index.js +2 -0
- package/esm/listeners/browser.js +1 -3
- package/esm/sdkClient/client.js +14 -11
- package/esm/sdkClient/sdkClient.js +1 -1
- package/esm/sdkFactory/index.js +10 -15
- package/esm/sdkManager/index.js +2 -1
- package/esm/storages/inLocalStorage/index.js +5 -4
- package/esm/storages/inMemory/InMemoryStorage.js +6 -8
- package/esm/storages/inMemory/InMemoryStorageCS.js +7 -8
- package/esm/storages/inRedis/index.js +10 -14
- package/esm/storages/pluggable/index.js +16 -20
- package/esm/sync/submitters/impressionCountsSubmitter.js +2 -4
- package/esm/sync/submitters/submitterManager.js +3 -6
- package/esm/trackers/impressionsTracker.js +17 -18
- package/esm/trackers/strategy/strategyDebug.js +4 -11
- package/esm/trackers/strategy/strategyNone.js +11 -16
- package/esm/trackers/strategy/strategyOptimized.js +11 -21
- package/package.json +1 -1
- package/src/dtos/types.ts +2 -1
- package/src/evaluator/index.ts +2 -0
- package/src/evaluator/types.ts +1 -1
- package/src/listeners/browser.ts +1 -3
- package/src/sdkClient/client.ts +19 -15
- package/src/sdkClient/sdkClient.ts +1 -1
- package/src/sdkFactory/index.ts +11 -16
- package/src/sdkFactory/types.ts +1 -1
- package/src/sdkManager/index.ts +2 -1
- package/src/storages/inLocalStorage/index.ts +5 -4
- package/src/storages/inMemory/InMemoryStorage.ts +6 -6
- package/src/storages/inMemory/InMemoryStorageCS.ts +7 -6
- package/src/storages/inRedis/index.ts +10 -10
- package/src/storages/pluggable/index.ts +16 -20
- package/src/storages/types.ts +2 -2
- package/src/sync/submitters/impressionCountsSubmitter.ts +2 -4
- package/src/sync/submitters/submitterManager.ts +3 -4
- package/src/sync/submitters/uniqueKeysSubmitter.ts +2 -3
- package/src/trackers/impressionsTracker.ts +18 -19
- package/src/trackers/strategy/strategyDebug.ts +4 -11
- package/src/trackers/strategy/strategyNone.ts +11 -17
- package/src/trackers/strategy/strategyOptimized.ts +10 -20
- package/src/trackers/types.ts +13 -8
- package/types/splitio.d.ts +4 -0
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* None strategy for impressions tracker.
|
|
3
3
|
*
|
|
4
|
-
* @param
|
|
4
|
+
* @param impressionCounts - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
|
|
5
5
|
* @param uniqueKeysTracker - unique keys tracker in charge of tracking the unique keys per split.
|
|
6
|
-
* @returns
|
|
6
|
+
* @returns None strategy
|
|
7
7
|
*/
|
|
8
|
-
export function strategyNoneFactory(
|
|
8
|
+
export function strategyNoneFactory(impressionCounts, uniqueKeysTracker) {
|
|
9
9
|
return {
|
|
10
|
-
process: function (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
impressionsToStore: [],
|
|
20
|
-
impressionsToListener: impressions,
|
|
21
|
-
deduped: 0
|
|
22
|
-
};
|
|
10
|
+
process: function (impression) {
|
|
11
|
+
var now = Date.now();
|
|
12
|
+
// Increments impression counter per featureName
|
|
13
|
+
impressionCounts.track(impression.feature, now, 1);
|
|
14
|
+
// Keep track by unique key
|
|
15
|
+
uniqueKeysTracker.track(impression.keyName, impression.feature);
|
|
16
|
+
// Do not store impressions
|
|
17
|
+
return false;
|
|
23
18
|
}
|
|
24
19
|
};
|
|
25
20
|
}
|
|
@@ -3,29 +3,19 @@ import { truncateTimeFrame } from '../../utils/time';
|
|
|
3
3
|
* Optimized strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
|
|
4
4
|
*
|
|
5
5
|
* @param impressionsObserver - impression observer. previous time (pt property) is included in impression instances
|
|
6
|
-
* @param
|
|
7
|
-
* @returns
|
|
6
|
+
* @param impressionCounts - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
|
|
7
|
+
* @returns Optimized strategy
|
|
8
8
|
*/
|
|
9
|
-
export function strategyOptimizedFactory(impressionsObserver,
|
|
9
|
+
export function strategyOptimizedFactory(impressionsObserver, impressionCounts) {
|
|
10
10
|
return {
|
|
11
|
-
process: function (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// Checks if the impression should be added in queue to be sent
|
|
20
|
-
if (!impression.pt || impression.pt < truncateTimeFrame(now)) {
|
|
21
|
-
impressionsToStore.push(impression);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
return {
|
|
25
|
-
impressionsToStore: impressionsToStore,
|
|
26
|
-
impressionsToListener: impressions,
|
|
27
|
-
deduped: impressions.length - impressionsToStore.length
|
|
28
|
-
};
|
|
11
|
+
process: function (impression) {
|
|
12
|
+
impression.pt = impressionsObserver.testAndSet(impression);
|
|
13
|
+
var now = Date.now();
|
|
14
|
+
// Increments impression counter per featureName
|
|
15
|
+
if (impression.pt)
|
|
16
|
+
impressionCounts.track(impression.feature, now, 1);
|
|
17
|
+
// Checks if the impression should be added in queue to be sent
|
|
18
|
+
return (!impression.pt || impression.pt < truncateTimeFrame(now)) ? true : false;
|
|
29
19
|
}
|
|
30
20
|
};
|
|
31
21
|
}
|
package/package.json
CHANGED
package/src/dtos/types.ts
CHANGED
package/src/evaluator/index.ts
CHANGED
|
@@ -156,12 +156,14 @@ function getEvaluation(
|
|
|
156
156
|
return evaluation.then(result => {
|
|
157
157
|
result.changeNumber = split.getChangeNumber();
|
|
158
158
|
result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
|
|
159
|
+
result.impressionsDisabled = splitJSON.impressionsDisabled;
|
|
159
160
|
|
|
160
161
|
return result;
|
|
161
162
|
});
|
|
162
163
|
} else {
|
|
163
164
|
evaluation.changeNumber = split.getChangeNumber(); // Always sync and optional
|
|
164
165
|
evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
|
|
166
|
+
evaluation.impressionsDisabled = splitJSON.impressionsDisabled;
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
|
package/src/evaluator/types.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface IEvaluation {
|
|
|
25
25
|
config?: string | null
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export type IEvaluationResult = IEvaluation & { treatment: string }
|
|
28
|
+
export type IEvaluationResult = IEvaluation & { treatment: string; impressionsDisabled?: boolean }
|
|
29
29
|
|
|
30
30
|
export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
|
|
31
31
|
|
package/src/listeners/browser.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { IResponse, ISplitApi } from '../services/types';
|
|
|
8
8
|
import { ISettings } from '../types';
|
|
9
9
|
import SplitIO from '../../types/splitio';
|
|
10
10
|
import { ImpressionsPayload } from '../sync/submitters/types';
|
|
11
|
-
import { OPTIMIZED, DEBUG, NONE } from '../utils/constants';
|
|
12
11
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
13
12
|
import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
|
|
14
13
|
import { ISyncManager } from '../sync/types';
|
|
@@ -78,10 +77,9 @@ export class BrowserSignalListener implements ISignalListener {
|
|
|
78
77
|
|
|
79
78
|
// Flush impressions & events data if there is user consent
|
|
80
79
|
if (isConsentGranted(this.settings)) {
|
|
81
|
-
const sim = this.settings.sync.impressionsMode;
|
|
82
80
|
const extraMetadata = {
|
|
83
81
|
// sim stands for Sync/Split Impressions Mode
|
|
84
|
-
sim:
|
|
82
|
+
sim: this.settings.sync.impressionsMode
|
|
85
83
|
};
|
|
86
84
|
|
|
87
85
|
this._flushData(events + '/testImpressions/beacon', this.storage.impressions, this.serviceApi.postTestImpressionsBulk, this.fromImpressionsCollector, extraMetadata);
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
|
|
|
11
11
|
import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
12
12
|
import { isConsumerMode } from '../utils/settingsValidation/mode';
|
|
13
13
|
import { Method } from '../sync/submitters/types';
|
|
14
|
+
import { ImpressionDecorated } from '../trackers/types';
|
|
14
15
|
|
|
15
16
|
const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
|
|
16
17
|
|
|
@@ -34,11 +35,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
34
35
|
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
|
|
35
36
|
|
|
36
37
|
const wrapUp = (evaluationResult: IEvaluationResult) => {
|
|
37
|
-
const queue:
|
|
38
|
+
const queue: ImpressionDecorated[] = [];
|
|
38
39
|
const treatment = processEvaluation(evaluationResult, featureFlagName, key, attributes, withConfig, methodName, queue);
|
|
39
40
|
impressionsTracker.track(queue, attributes);
|
|
40
41
|
|
|
41
|
-
stopTelemetryTracker(queue[0] && queue[0].label);
|
|
42
|
+
stopTelemetryTracker(queue[0] && queue[0].imp.label);
|
|
42
43
|
return treatment;
|
|
43
44
|
};
|
|
44
45
|
|
|
@@ -59,14 +60,14 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
59
60
|
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENTS_WITH_CONFIG : TREATMENTS);
|
|
60
61
|
|
|
61
62
|
const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
|
|
62
|
-
const queue:
|
|
63
|
+
const queue: ImpressionDecorated[] = [];
|
|
63
64
|
const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
|
|
64
65
|
Object.keys(evaluationResults).forEach(featureFlagName => {
|
|
65
66
|
treatments[featureFlagName] = processEvaluation(evaluationResults[featureFlagName], featureFlagName, key, attributes, withConfig, methodName, queue);
|
|
66
67
|
});
|
|
67
68
|
impressionsTracker.track(queue, attributes);
|
|
68
69
|
|
|
69
|
-
stopTelemetryTracker(queue[0] && queue[0].label);
|
|
70
|
+
stopTelemetryTracker(queue[0] && queue[0].imp.label);
|
|
70
71
|
return treatments;
|
|
71
72
|
};
|
|
72
73
|
|
|
@@ -87,7 +88,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
87
88
|
const stopTelemetryTracker = telemetryTracker.trackEval(method);
|
|
88
89
|
|
|
89
90
|
const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
|
|
90
|
-
const queue:
|
|
91
|
+
const queue: ImpressionDecorated[] = [];
|
|
91
92
|
const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
|
|
92
93
|
const evaluations = evaluationResults;
|
|
93
94
|
Object.keys(evaluations).forEach(featureFlagName => {
|
|
@@ -95,7 +96,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
95
96
|
});
|
|
96
97
|
impressionsTracker.track(queue, attributes);
|
|
97
98
|
|
|
98
|
-
stopTelemetryTracker(queue[0] && queue[0].label);
|
|
99
|
+
stopTelemetryTracker(queue[0] && queue[0].imp.label);
|
|
99
100
|
return treatments;
|
|
100
101
|
};
|
|
101
102
|
|
|
@@ -128,24 +129,27 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
128
129
|
attributes: SplitIO.Attributes | undefined,
|
|
129
130
|
withConfig: boolean,
|
|
130
131
|
invokingMethodName: string,
|
|
131
|
-
queue:
|
|
132
|
+
queue: ImpressionDecorated[]
|
|
132
133
|
): SplitIO.Treatment | SplitIO.TreatmentWithConfig {
|
|
133
134
|
const matchingKey = getMatching(key);
|
|
134
135
|
const bucketingKey = getBucketing(key);
|
|
135
136
|
|
|
136
|
-
const { treatment, label, changeNumber, config = null } = evaluation;
|
|
137
|
+
const { treatment, label, changeNumber, config = null, impressionsDisabled } = evaluation;
|
|
137
138
|
log.info(IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
|
|
138
139
|
|
|
139
140
|
if (validateSplitExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) {
|
|
140
141
|
log.info(IMPRESSION_QUEUEING);
|
|
141
142
|
queue.push({
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
imp: {
|
|
144
|
+
feature: featureFlagName,
|
|
145
|
+
keyName: matchingKey,
|
|
146
|
+
treatment,
|
|
147
|
+
time: Date.now(),
|
|
148
|
+
bucketingKey,
|
|
149
|
+
label,
|
|
150
|
+
changeNumber: changeNumber as number,
|
|
151
|
+
},
|
|
152
|
+
disabled: impressionsDisabled
|
|
149
153
|
});
|
|
150
154
|
}
|
|
151
155
|
|
|
@@ -61,7 +61,7 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
|
|
|
61
61
|
releaseApiKey(settings.core.authorizationKey);
|
|
62
62
|
telemetryTracker.sessionLength();
|
|
63
63
|
signalListener && signalListener.stop();
|
|
64
|
-
uniqueKeysTracker
|
|
64
|
+
uniqueKeysTracker.stop();
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// Stop background jobs
|
package/src/sdkFactory/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
|
|
|
13
13
|
import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
|
|
14
14
|
import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
|
|
15
15
|
import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
|
|
16
|
-
import {
|
|
16
|
+
import { DEBUG, OPTIMIZED } from '../utils/constants';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Modular SDK factory
|
|
@@ -59,21 +59,16 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
|
|
|
59
59
|
const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
|
|
60
60
|
|
|
61
61
|
const observer = impressionsObserverFactory();
|
|
62
|
-
const uniqueKeysTracker =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
strategy = strategyNoneFactory(storage.impressionCounts!, uniqueKeysTracker!);
|
|
71
|
-
break;
|
|
72
|
-
default:
|
|
73
|
-
strategy = strategyDebugFactory(observer);
|
|
74
|
-
}
|
|
62
|
+
const uniqueKeysTracker = uniqueKeysTrackerFactory(log, storage.uniqueKeys, filterAdapterFactory && filterAdapterFactory());
|
|
63
|
+
|
|
64
|
+
const noneStrategy = strategyNoneFactory(storage.impressionCounts, uniqueKeysTracker);
|
|
65
|
+
const strategy = impressionsMode === OPTIMIZED ?
|
|
66
|
+
strategyOptimizedFactory(observer, storage.impressionCounts) :
|
|
67
|
+
impressionsMode === DEBUG ?
|
|
68
|
+
strategyDebugFactory(observer) :
|
|
69
|
+
noneStrategy;
|
|
75
70
|
|
|
76
|
-
const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, strategy, whenInit, integrationsManager, storage.telemetry);
|
|
71
|
+
const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, noneStrategy, strategy, whenInit, integrationsManager, storage.telemetry);
|
|
77
72
|
const eventTracker = eventTrackerFactory(settings, storage.events, whenInit, integrationsManager, storage.telemetry);
|
|
78
73
|
|
|
79
74
|
// splitApi is used by SyncManager and Browser signal listener
|
|
@@ -99,7 +94,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
|
|
|
99
94
|
// We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
|
|
100
95
|
validateAndTrackApiKey(log, settings.core.authorizationKey);
|
|
101
96
|
readiness.init();
|
|
102
|
-
uniqueKeysTracker
|
|
97
|
+
uniqueKeysTracker.start();
|
|
103
98
|
syncManager && syncManager.start();
|
|
104
99
|
signalListener && signalListener.start();
|
|
105
100
|
|
package/src/sdkFactory/types.ts
CHANGED
|
@@ -46,7 +46,7 @@ export interface ISdkFactoryContext {
|
|
|
46
46
|
eventTracker: IEventTracker,
|
|
47
47
|
telemetryTracker: ITelemetryTracker,
|
|
48
48
|
storage: IStorageSync | IStorageAsync,
|
|
49
|
-
uniqueKeysTracker
|
|
49
|
+
uniqueKeysTracker: IUniqueKeysTracker,
|
|
50
50
|
signalListener?: ISignalListener
|
|
51
51
|
splitApi?: ISplitApi
|
|
52
52
|
syncManager?: ISyncManager,
|
package/src/sdkManager/index.ts
CHANGED
|
@@ -31,7 +31,8 @@ function objectToView(splitObject: ISplit | null): SplitIO.SplitView | null {
|
|
|
31
31
|
treatments: collectTreatments(splitObject),
|
|
32
32
|
configs: splitObject.configurations || {},
|
|
33
33
|
sets: splitObject.sets || [],
|
|
34
|
-
defaultTreatment: splitObject.defaultTreatment
|
|
34
|
+
defaultTreatment: splitObject.defaultTreatment,
|
|
35
|
+
impressionsDisabled: splitObject.impressionsDisabled === true
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -9,7 +9,7 @@ import { SplitsCacheInLocal } from './SplitsCacheInLocal';
|
|
|
9
9
|
import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
|
|
10
10
|
import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
|
|
11
11
|
import { LOG_PREFIX } from './constants';
|
|
12
|
-
import {
|
|
12
|
+
import { STORAGE_LOCALSTORAGE } from '../../utils/constants';
|
|
13
13
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
|
|
14
14
|
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
|
|
15
15
|
import { getMatching } from '../../utils/key';
|
|
@@ -31,7 +31,7 @@ export function InLocalStorage(options: SplitIO.InLocalStorageOptions = {}): ISt
|
|
|
31
31
|
return InMemoryStorageCSFactory(params);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize
|
|
34
|
+
const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
|
|
35
35
|
const matchingKey = getMatching(settings.core.key);
|
|
36
36
|
const keys = new KeyBuilderCS(prefix, matchingKey);
|
|
37
37
|
|
|
@@ -44,10 +44,10 @@ export function InLocalStorage(options: SplitIO.InLocalStorageOptions = {}): ISt
|
|
|
44
44
|
segments,
|
|
45
45
|
largeSegments,
|
|
46
46
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
47
|
-
impressionCounts:
|
|
47
|
+
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
48
48
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
49
49
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
50
|
-
uniqueKeys:
|
|
50
|
+
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
51
51
|
|
|
52
52
|
validateCache() {
|
|
53
53
|
return validateCache(options, settings, keys, splits, segments, largeSegments);
|
|
@@ -66,6 +66,7 @@ export function InLocalStorage(options: SplitIO.InLocalStorageOptions = {}): ISt
|
|
|
66
66
|
impressionCounts: this.impressionCounts,
|
|
67
67
|
events: this.events,
|
|
68
68
|
telemetry: this.telemetry,
|
|
69
|
+
uniqueKeys: this.uniqueKeys,
|
|
69
70
|
|
|
70
71
|
destroy() { }
|
|
71
72
|
};
|
|
@@ -4,7 +4,7 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
|
|
|
4
4
|
import { EventsCacheInMemory } from './EventsCacheInMemory';
|
|
5
5
|
import { IStorageFactoryParams, IStorageSync } from '../types';
|
|
6
6
|
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
7
|
-
import {
|
|
7
|
+
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
9
9
|
import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
|
|
|
14
14
|
* @param params - parameters required by EventsCacheSync
|
|
15
15
|
*/
|
|
16
16
|
export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageSync {
|
|
17
|
-
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: {
|
|
17
|
+
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { __splitFiltersValidation } } } = params;
|
|
18
18
|
|
|
19
19
|
const splits = new SplitsCacheInMemory(__splitFiltersValidation);
|
|
20
20
|
const segments = new SegmentsCacheInMemory();
|
|
@@ -23,10 +23,10 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
|
|
|
23
23
|
splits,
|
|
24
24
|
segments,
|
|
25
25
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
26
|
-
impressionCounts:
|
|
26
|
+
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
27
27
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
28
28
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
29
|
-
uniqueKeys:
|
|
29
|
+
uniqueKeys: new UniqueKeysCacheInMemory(),
|
|
30
30
|
|
|
31
31
|
destroy() { }
|
|
32
32
|
};
|
|
@@ -37,8 +37,8 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
|
|
|
37
37
|
const noopTrack = () => true;
|
|
38
38
|
storage.impressions.track = noopTrack;
|
|
39
39
|
storage.events.track = noopTrack;
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
storage.impressionCounts.track = noopTrack;
|
|
41
|
+
storage.uniqueKeys.track = noopTrack;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
return storage;
|
|
@@ -4,7 +4,7 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
|
|
|
4
4
|
import { EventsCacheInMemory } from './EventsCacheInMemory';
|
|
5
5
|
import { IStorageSync, IStorageFactoryParams } from '../types';
|
|
6
6
|
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
7
|
-
import {
|
|
7
|
+
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
9
9
|
import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
|
|
|
14
14
|
* @param params - parameters required by EventsCacheSync
|
|
15
15
|
*/
|
|
16
16
|
export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSync {
|
|
17
|
-
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize
|
|
17
|
+
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize }, sync: { __splitFiltersValidation } } } = params;
|
|
18
18
|
|
|
19
19
|
const splits = new SplitsCacheInMemory(__splitFiltersValidation);
|
|
20
20
|
const segments = new MySegmentsCacheInMemory();
|
|
@@ -25,10 +25,10 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
25
25
|
segments,
|
|
26
26
|
largeSegments,
|
|
27
27
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
28
|
-
impressionCounts:
|
|
28
|
+
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
29
29
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
30
30
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
31
|
-
uniqueKeys:
|
|
31
|
+
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
32
32
|
|
|
33
33
|
destroy() { },
|
|
34
34
|
|
|
@@ -42,6 +42,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
42
42
|
impressionCounts: this.impressionCounts,
|
|
43
43
|
events: this.events,
|
|
44
44
|
telemetry: this.telemetry,
|
|
45
|
+
uniqueKeys: this.uniqueKeys,
|
|
45
46
|
|
|
46
47
|
destroy() { }
|
|
47
48
|
};
|
|
@@ -54,8 +55,8 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
54
55
|
const noopTrack = () => true;
|
|
55
56
|
storage.impressions.track = noopTrack;
|
|
56
57
|
storage.events.track = noopTrack;
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
storage.impressionCounts.track = noopTrack;
|
|
59
|
+
storage.uniqueKeys.track = noopTrack;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
return storage;
|
|
@@ -6,7 +6,7 @@ import { SplitsCacheInRedis } from './SplitsCacheInRedis';
|
|
|
6
6
|
import { SegmentsCacheInRedis } from './SegmentsCacheInRedis';
|
|
7
7
|
import { ImpressionsCacheInRedis } from './ImpressionsCacheInRedis';
|
|
8
8
|
import { EventsCacheInRedis } from './EventsCacheInRedis';
|
|
9
|
-
import {
|
|
9
|
+
import { STORAGE_REDIS } from '../../utils/constants';
|
|
10
10
|
import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
|
|
11
11
|
import { UniqueKeysCacheInRedis } from './UniqueKeysCacheInRedis';
|
|
12
12
|
import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
|
|
@@ -30,19 +30,19 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
|
|
|
30
30
|
const prefix = validatePrefix(options.prefix);
|
|
31
31
|
|
|
32
32
|
function InRedisStorageFactory(params: IStorageFactoryParams): IStorageAsync {
|
|
33
|
-
const { onReadyCb, settings, settings: { log
|
|
33
|
+
const { onReadyCb, settings, settings: { log } } = params;
|
|
34
34
|
const metadata = metadataBuilder(settings);
|
|
35
35
|
const keys = new KeyBuilderSS(prefix, metadata);
|
|
36
36
|
const redisClient: RedisAdapter = new RD(log, options.options || {});
|
|
37
37
|
const telemetry = new TelemetryCacheInRedis(log, keys, redisClient);
|
|
38
|
-
const impressionCountsCache =
|
|
39
|
-
const uniqueKeysCache =
|
|
38
|
+
const impressionCountsCache = new ImpressionCountsCacheInRedis(log, keys.buildImpressionsCountKey(), redisClient);
|
|
39
|
+
const uniqueKeysCache = new UniqueKeysCacheInRedis(log, keys.buildUniqueKeysKey(), redisClient);
|
|
40
40
|
|
|
41
41
|
// subscription to Redis connect event in order to emit SDK_READY event on consumer mode
|
|
42
42
|
redisClient.on('connect', () => {
|
|
43
43
|
onReadyCb();
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
impressionCountsCache.start();
|
|
45
|
+
uniqueKeysCache.start();
|
|
46
46
|
|
|
47
47
|
// Synchronize config
|
|
48
48
|
telemetry.recordConfig();
|
|
@@ -60,10 +60,10 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
|
|
|
60
60
|
// When using REDIS we should:
|
|
61
61
|
// 1- Disconnect from the storage
|
|
62
62
|
destroy(): Promise<void> {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
return Promise.all([
|
|
64
|
+
impressionCountsCache.stop(),
|
|
65
|
+
uniqueKeysCache.stop()
|
|
66
|
+
]).then(() => { redisClient.disconnect(); });
|
|
67
67
|
// @TODO check that caches works as expected when redisClient is disconnected
|
|
68
68
|
}
|
|
69
69
|
};
|
|
@@ -8,7 +8,7 @@ import { EventsCachePluggable } from './EventsCachePluggable';
|
|
|
8
8
|
import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter';
|
|
9
9
|
import { isObject } from '../../utils/lang';
|
|
10
10
|
import { getStorageHash, validatePrefix } from '../KeyBuilder';
|
|
11
|
-
import { CONSUMER_PARTIAL_MODE,
|
|
11
|
+
import { CONSUMER_PARTIAL_MODE, 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';
|
|
@@ -63,35 +63,31 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
63
63
|
const prefix = validatePrefix(options.prefix);
|
|
64
64
|
|
|
65
65
|
function PluggableStorageFactory(params: IStorageFactoryParams): IStorageAsync {
|
|
66
|
-
const { onReadyCb, settings, settings: { log, mode,
|
|
66
|
+
const { onReadyCb, settings, settings: { log, mode, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
|
|
67
67
|
const metadata = metadataBuilder(settings);
|
|
68
68
|
const keys = new KeyBuilderSS(prefix, metadata);
|
|
69
69
|
const wrapper = wrapperAdapter(log, options.wrapper);
|
|
70
70
|
|
|
71
|
-
const
|
|
71
|
+
const isSynchronizer = mode === undefined; // If mode is not defined, the synchronizer is running
|
|
72
72
|
const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
|
|
73
73
|
|
|
74
|
-
const telemetry = shouldRecordTelemetry(params) ||
|
|
74
|
+
const telemetry = shouldRecordTelemetry(params) || isSynchronizer ?
|
|
75
75
|
isPartialConsumer ?
|
|
76
76
|
new TelemetryCacheInMemory() :
|
|
77
77
|
new TelemetryCachePluggable(log, keys, wrapper) :
|
|
78
78
|
undefined;
|
|
79
79
|
|
|
80
|
-
const impressionCountsCache =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper) :
|
|
84
|
-
undefined;
|
|
80
|
+
const impressionCountsCache = isPartialConsumer ?
|
|
81
|
+
new ImpressionCountsCacheInMemory() :
|
|
82
|
+
new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper);
|
|
85
83
|
|
|
86
|
-
const uniqueKeysCache =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
|
|
90
|
-
undefined;
|
|
84
|
+
const uniqueKeysCache = isPartialConsumer ?
|
|
85
|
+
settings.core.key === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
|
|
86
|
+
new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper);
|
|
91
87
|
|
|
92
88
|
// Connects to wrapper and emits SDK_READY event on main client
|
|
93
89
|
const connectPromise = wrapper.connect().then(() => {
|
|
94
|
-
if (
|
|
90
|
+
if (isSynchronizer) {
|
|
95
91
|
// @TODO reuse InLocalStorage::validateCache logic
|
|
96
92
|
// In standalone or producer mode, clear storage if SDK key, flags filter criteria or flags spec version was modified
|
|
97
93
|
return wrapper.get(keys.buildHashKey()).then((hash) => {
|
|
@@ -107,8 +103,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
107
103
|
});
|
|
108
104
|
} else {
|
|
109
105
|
// Start periodic flush of async storages if not running synchronizer (producer mode)
|
|
110
|
-
if (
|
|
111
|
-
if (
|
|
106
|
+
if ((impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
|
|
107
|
+
if ((uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
|
|
112
108
|
if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig) (telemetry as ITelemetryCacheAsync).recordConfig();
|
|
113
109
|
|
|
114
110
|
onReadyCb();
|
|
@@ -130,9 +126,9 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
130
126
|
|
|
131
127
|
// Stop periodic flush and disconnect the underlying storage
|
|
132
128
|
destroy() {
|
|
133
|
-
return Promise.all(
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
return Promise.all(isSynchronizer ? [] : [
|
|
130
|
+
(impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
|
|
131
|
+
(uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
|
|
136
132
|
]).then(() => wrapper.disconnect());
|
|
137
133
|
},
|
|
138
134
|
|
package/src/storages/types.ts
CHANGED
|
@@ -435,10 +435,10 @@ export interface IStorageBase<
|
|
|
435
435
|
splits: TSplitsCache,
|
|
436
436
|
segments: TSegmentsCache,
|
|
437
437
|
impressions: TImpressionsCache,
|
|
438
|
-
impressionCounts
|
|
438
|
+
impressionCounts: TImpressionsCountCache,
|
|
439
439
|
events: TEventsCache,
|
|
440
440
|
telemetry?: TTelemetryCache,
|
|
441
|
-
uniqueKeys
|
|
441
|
+
uniqueKeys: TUniqueKeysCache,
|
|
442
442
|
destroy(): void | Promise<void>,
|
|
443
443
|
shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this
|
|
444
444
|
}
|
|
@@ -39,8 +39,6 @@ export function impressionCountsSubmitterFactory(params: ISdkFactoryContextSync)
|
|
|
39
39
|
storage: { impressionCounts }
|
|
40
40
|
} = params;
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
|
|
45
|
-
}
|
|
42
|
+
// retry impressions counts only once.
|
|
43
|
+
return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
|
|
46
44
|
}
|
|
@@ -10,13 +10,12 @@ export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmit
|
|
|
10
10
|
|
|
11
11
|
const submitters = [
|
|
12
12
|
impressionsSubmitterFactory(params),
|
|
13
|
-
eventsSubmitterFactory(params)
|
|
13
|
+
eventsSubmitterFactory(params),
|
|
14
|
+
impressionCountsSubmitterFactory(params),
|
|
15
|
+
uniqueKeysSubmitterFactory(params)
|
|
14
16
|
];
|
|
15
17
|
|
|
16
|
-
const impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
|
|
17
|
-
if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter);
|
|
18
18
|
const telemetrySubmitter = telemetrySubmitterFactory(params);
|
|
19
|
-
if (params.storage.uniqueKeys) submitters.push(uniqueKeysSubmitterFactory(params));
|
|
20
19
|
|
|
21
20
|
return {
|
|
22
21
|
// `onlyTelemetry` true if SDK is created with userConsent not GRANTED
|