@splitsoftware/splitio-commons 2.0.3-rc.0 → 2.0.3
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 +2 -3
- package/LICENSE +1 -1
- package/README.md +2 -2
- package/cjs/evaluator/index.js +0 -2
- package/cjs/evaluator/matchers/large_segment.js +0 -6
- package/cjs/evaluator/matchers/segment.js +0 -6
- package/cjs/listeners/browser.js +6 -4
- package/cjs/listeners/node.js +2 -2
- package/cjs/sdkClient/client.js +13 -13
- package/cjs/sdkClient/sdkClient.js +1 -1
- package/cjs/sdkFactory/index.js +14 -9
- package/cjs/sdkManager/index.js +1 -2
- package/cjs/storages/inLocalStorage/index.js +3 -4
- package/cjs/storages/inMemory/InMemoryStorage.js +3 -3
- package/cjs/storages/inMemory/InMemoryStorageCS.js +3 -4
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/index.js +13 -9
- package/cjs/storages/pluggable/index.js +19 -15
- package/cjs/sync/streaming/pushManager.js +8 -6
- package/cjs/sync/submitters/impressionCountsSubmitter.js +4 -2
- package/cjs/sync/submitters/submitterManager.js +6 -3
- package/cjs/sync/syncManagerOnline.js +2 -1
- package/cjs/trackers/eventTracker.js +1 -1
- package/cjs/trackers/impressionsTracker.js +19 -18
- package/cjs/trackers/strategy/strategyDebug.js +11 -4
- package/cjs/trackers/strategy/strategyNone.js +16 -11
- package/cjs/trackers/strategy/strategyOptimized.js +21 -11
- package/cjs/utils/settingsValidation/index.js +1 -1
- package/esm/evaluator/index.js +0 -2
- package/esm/evaluator/matchers/large_segment.js +0 -6
- package/esm/evaluator/matchers/segment.js +0 -6
- package/esm/listeners/browser.js +3 -1
- package/esm/listeners/node.js +2 -2
- package/esm/sdkClient/client.js +13 -13
- package/esm/sdkClient/sdkClient.js +1 -1
- package/esm/sdkFactory/index.js +15 -10
- package/esm/sdkManager/index.js +1 -2
- package/esm/storages/inLocalStorage/index.js +4 -5
- package/esm/storages/inMemory/InMemoryStorage.js +4 -4
- package/esm/storages/inMemory/InMemoryStorageCS.js +4 -5
- package/esm/storages/inRedis/SplitsCacheInRedis.js +1 -1
- package/esm/storages/inRedis/index.js +14 -10
- package/esm/storages/pluggable/index.js +20 -16
- package/esm/sync/streaming/pushManager.js +8 -6
- package/esm/sync/submitters/impressionCountsSubmitter.js +4 -2
- package/esm/sync/submitters/submitterManager.js +6 -3
- package/esm/sync/syncManagerOnline.js +2 -1
- package/esm/trackers/eventTracker.js +1 -1
- package/esm/trackers/impressionsTracker.js +19 -18
- package/esm/trackers/strategy/strategyDebug.js +11 -4
- package/esm/trackers/strategy/strategyNone.js +16 -11
- package/esm/trackers/strategy/strategyOptimized.js +21 -11
- package/esm/utils/settingsValidation/index.js +1 -1
- package/package.json +1 -1
- package/src/dtos/types.ts +1 -2
- package/src/evaluator/index.ts +0 -2
- package/src/evaluator/matchers/large_segment.ts +0 -7
- package/src/evaluator/matchers/segment.ts +0 -7
- package/src/evaluator/types.ts +1 -1
- package/src/listeners/browser.ts +3 -1
- package/src/listeners/node.ts +2 -2
- package/src/sdkClient/client.ts +11 -11
- package/src/sdkClient/sdkClient.ts +1 -1
- package/src/sdkFactory/index.ts +16 -11
- package/src/sdkFactory/types.ts +1 -1
- package/src/sdkManager/index.ts +1 -2
- package/src/storages/inLocalStorage/index.ts +4 -5
- package/src/storages/inMemory/InMemoryStorage.ts +4 -4
- package/src/storages/inMemory/InMemoryStorageCS.ts +4 -5
- package/src/storages/inRedis/SplitsCacheInRedis.ts +1 -1
- package/src/storages/inRedis/index.ts +10 -10
- package/src/storages/pluggable/index.ts +20 -16
- package/src/storages/types.ts +2 -2
- package/src/sync/streaming/pushManager.ts +8 -6
- package/src/sync/submitters/impressionCountsSubmitter.ts +4 -2
- package/src/sync/submitters/submitterManager.ts +4 -3
- package/src/sync/submitters/uniqueKeysSubmitter.ts +3 -2
- package/src/sync/syncManagerOnline.ts +2 -2
- package/src/trackers/eventTracker.ts +1 -1
- package/src/trackers/impressionsTracker.ts +19 -18
- package/src/trackers/strategy/strategyDebug.ts +11 -4
- package/src/trackers/strategy/strategyNone.ts +17 -11
- package/src/trackers/strategy/strategyOptimized.ts +20 -10
- package/src/trackers/types.ts +8 -2
- package/src/utils/settingsValidation/index.ts +1 -1
- package/types/index.d.ts +1 -1
- package/types/splitio.d.ts +1 -5
|
@@ -10,7 +10,7 @@ import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
|
|
|
10
10
|
import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
|
|
11
11
|
import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
|
|
12
12
|
import { LOG_PREFIX } from './constants';
|
|
13
|
-
import { STORAGE_LOCALSTORAGE } from '../../utils/constants';
|
|
13
|
+
import { DEBUG, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
|
|
14
14
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
|
|
15
15
|
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
|
|
16
16
|
import { getMatching } from '../../utils/key';
|
|
@@ -34,7 +34,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
|
|
|
34
34
|
return InMemoryStorageCSFactory(params);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
|
|
37
|
+
const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode } } } = params;
|
|
38
38
|
const matchingKey = getMatching(settings.core.key);
|
|
39
39
|
const keys = new KeyBuilderCS(prefix, matchingKey);
|
|
40
40
|
const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
|
|
@@ -48,10 +48,10 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
|
|
|
48
48
|
segments,
|
|
49
49
|
largeSegments,
|
|
50
50
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
51
|
-
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
51
|
+
impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
|
|
52
52
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
53
53
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
54
|
-
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
54
|
+
uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
|
|
55
55
|
|
|
56
56
|
destroy() { },
|
|
57
57
|
|
|
@@ -66,7 +66,6 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
|
|
|
66
66
|
impressionCounts: this.impressionCounts,
|
|
67
67
|
events: this.events,
|
|
68
68
|
telemetry: this.telemetry,
|
|
69
|
-
uniqueKeys: this.uniqueKeys,
|
|
70
69
|
|
|
71
70
|
destroy() { }
|
|
72
71
|
};
|
|
@@ -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 { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
7
|
+
import { DEBUG, LOCALHOST_MODE, NONE, 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: { __splitFiltersValidation } } } = params;
|
|
17
|
+
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __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: new ImpressionCountsCacheInMemory(),
|
|
26
|
+
impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
|
|
27
27
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
28
28
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
29
|
-
uniqueKeys: new UniqueKeysCacheInMemory(),
|
|
29
|
+
uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemory() : undefined,
|
|
30
30
|
|
|
31
31
|
destroy() { }
|
|
32
32
|
};
|
|
@@ -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 { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
7
|
+
import { DEBUG, LOCALHOST_MODE, NONE, 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 }, sync: { __splitFiltersValidation } } } = params;
|
|
17
|
+
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __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: new ImpressionCountsCacheInMemory(),
|
|
28
|
+
impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
|
|
29
29
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
30
30
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
31
|
-
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
31
|
+
uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
|
|
32
32
|
|
|
33
33
|
destroy() { },
|
|
34
34
|
|
|
@@ -42,7 +42,6 @@ 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,
|
|
46
45
|
|
|
47
46
|
destroy() { }
|
|
48
47
|
};
|
|
@@ -266,10 +266,10 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
266
266
|
return Promise.reject(this.redisError);
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
const splits: Record<string, ISplit | null> = {};
|
|
270
269
|
const keys = names.map(name => this.keys.buildSplitKey(name));
|
|
271
270
|
return this.redis.mget(...keys)
|
|
272
271
|
.then(splitDefinitions => {
|
|
272
|
+
const splits: Record<string, ISplit | null> = {};
|
|
273
273
|
names.forEach((name, idx) => {
|
|
274
274
|
const split = splitDefinitions[idx];
|
|
275
275
|
splits[name] = split && JSON.parse(split);
|
|
@@ -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 { STORAGE_REDIS } from '../../utils/constants';
|
|
9
|
+
import { DEBUG, NONE, 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 } } = params;
|
|
33
|
+
const { onReadyCb, settings, settings: { log, sync: { impressionsMode } } } = 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 = new ImpressionCountsCacheInRedis(log, keys.buildImpressionsCountKey(), redisClient);
|
|
39
|
-
const uniqueKeysCache = new UniqueKeysCacheInRedis(log, keys.buildUniqueKeysKey(), redisClient);
|
|
38
|
+
const impressionCountsCache = impressionsMode !== DEBUG ? new ImpressionCountsCacheInRedis(log, keys.buildImpressionsCountKey(), redisClient) : undefined;
|
|
39
|
+
const uniqueKeysCache = impressionsMode === NONE ? new UniqueKeysCacheInRedis(log, keys.buildUniqueKeysKey(), redisClient) : undefined;
|
|
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
|
-
impressionCountsCache.start();
|
|
45
|
-
uniqueKeysCache.start();
|
|
44
|
+
if (impressionCountsCache) impressionCountsCache.start();
|
|
45
|
+
if (uniqueKeysCache) 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
|
+
let promises = [];
|
|
64
|
+
if (impressionCountsCache) promises.push(impressionCountsCache.stop());
|
|
65
|
+
if (uniqueKeysCache) promises.push(uniqueKeysCache.stop());
|
|
66
|
+
return Promise.all(promises).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, 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';
|
|
@@ -63,31 +63,35 @@ 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, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
|
|
66
|
+
const { onReadyCb, settings, settings: { log, mode, sync: { impressionsMode }, 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 isSyncronizer = 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) || isSyncronizer ?
|
|
75
75
|
isPartialConsumer ?
|
|
76
76
|
new TelemetryCacheInMemory() :
|
|
77
77
|
new TelemetryCachePluggable(log, keys, wrapper) :
|
|
78
78
|
undefined;
|
|
79
79
|
|
|
80
|
-
const impressionCountsCache =
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
const impressionCountsCache = impressionsMode !== DEBUG || isSyncronizer ?
|
|
81
|
+
isPartialConsumer ?
|
|
82
|
+
new ImpressionCountsCacheInMemory() :
|
|
83
|
+
new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper) :
|
|
84
|
+
undefined;
|
|
83
85
|
|
|
84
|
-
const uniqueKeysCache =
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
const uniqueKeysCache = impressionsMode === NONE || isSyncronizer ?
|
|
87
|
+
isPartialConsumer ?
|
|
88
|
+
settings.core.key === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
|
|
89
|
+
new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
|
|
90
|
+
undefined;
|
|
87
91
|
|
|
88
92
|
// Connects to wrapper and emits SDK_READY event on main client
|
|
89
93
|
const connectPromise = wrapper.connect().then(() => {
|
|
90
|
-
if (
|
|
94
|
+
if (isSyncronizer) {
|
|
91
95
|
// In standalone or producer mode, clear storage if SDK key or feature flag filter has changed
|
|
92
96
|
return wrapper.get(keys.buildHashKey()).then((hash) => {
|
|
93
97
|
const currentHash = getStorageHash(settings);
|
|
@@ -102,8 +106,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
102
106
|
});
|
|
103
107
|
} else {
|
|
104
108
|
// Start periodic flush of async storages if not running synchronizer (producer mode)
|
|
105
|
-
if ((impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
|
|
106
|
-
if ((uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
|
|
109
|
+
if (impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
|
|
110
|
+
if (uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
|
|
107
111
|
if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig) (telemetry as ITelemetryCacheAsync).recordConfig();
|
|
108
112
|
|
|
109
113
|
onReadyCb();
|
|
@@ -125,9 +129,9 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
125
129
|
|
|
126
130
|
// Stop periodic flush and disconnect the underlying storage
|
|
127
131
|
destroy() {
|
|
128
|
-
return Promise.all(
|
|
129
|
-
(impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
|
|
130
|
-
(uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
|
|
132
|
+
return Promise.all(isSyncronizer ? [] : [
|
|
133
|
+
impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
|
|
134
|
+
uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
|
|
131
135
|
]).then(() => wrapper.disconnect());
|
|
132
136
|
},
|
|
133
137
|
|
package/src/storages/types.ts
CHANGED
|
@@ -439,10 +439,10 @@ export interface IStorageBase<
|
|
|
439
439
|
splits: TSplitsCache,
|
|
440
440
|
segments: TSegmentsCache,
|
|
441
441
|
impressions: TImpressionsCache,
|
|
442
|
-
impressionCounts
|
|
442
|
+
impressionCounts?: TImpressionsCountCache,
|
|
443
443
|
events: TEventsCache,
|
|
444
444
|
telemetry?: TTelemetryCache,
|
|
445
|
-
uniqueKeys
|
|
445
|
+
uniqueKeys?: TUniqueKeysCache,
|
|
446
446
|
destroy(): void | Promise<void>,
|
|
447
447
|
shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this
|
|
448
448
|
}
|
|
@@ -349,12 +349,14 @@ export function pushManagerFactory(
|
|
|
349
349
|
// Reconnects in case of a new client.
|
|
350
350
|
// Run in next event-loop cycle to save authentication calls
|
|
351
351
|
// in case multiple clients are created in the current cycle.
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
connectForNewClient
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
352
|
+
if (this.isRunning()) {
|
|
353
|
+
setTimeout(function checkForReconnect() {
|
|
354
|
+
if (connectForNewClient) {
|
|
355
|
+
connectForNewClient = false;
|
|
356
|
+
connectPush();
|
|
357
|
+
}
|
|
358
|
+
}, 0);
|
|
359
|
+
}
|
|
358
360
|
}
|
|
359
361
|
},
|
|
360
362
|
// [Only for client-side]
|
|
@@ -39,6 +39,8 @@ export function impressionCountsSubmitterFactory(params: ISdkFactoryContextSync)
|
|
|
39
39
|
storage: { impressionCounts }
|
|
40
40
|
} = params;
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
if (impressionCounts) {
|
|
43
|
+
// retry impressions counts only once.
|
|
44
|
+
return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
|
|
45
|
+
}
|
|
44
46
|
}
|
|
@@ -10,12 +10,13 @@ export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmit
|
|
|
10
10
|
|
|
11
11
|
const submitters = [
|
|
12
12
|
impressionsSubmitterFactory(params),
|
|
13
|
-
eventsSubmitterFactory(params)
|
|
14
|
-
impressionCountsSubmitterFactory(params),
|
|
15
|
-
uniqueKeysSubmitterFactory(params)
|
|
13
|
+
eventsSubmitterFactory(params)
|
|
16
14
|
];
|
|
17
15
|
|
|
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));
|
|
19
20
|
|
|
20
21
|
return {
|
|
21
22
|
// `onlyTelemetry` true if SDK is created with userConsent not GRANTED
|
|
@@ -19,10 +19,10 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
19
19
|
const isClientSide = key !== undefined;
|
|
20
20
|
const postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
|
|
21
21
|
|
|
22
|
-
const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys
|
|
22
|
+
const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys!, UNIQUE_KEYS_RATE, DATA_NAME);
|
|
23
23
|
|
|
24
24
|
// register unique keys submitter to be executed when uniqueKeys cache is full
|
|
25
|
-
uniqueKeys
|
|
25
|
+
uniqueKeys!.setOnFullQueueCb(() => {
|
|
26
26
|
if (syncTask.isRunning()) {
|
|
27
27
|
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
|
|
28
28
|
syncTask.execute();
|
|
@@ -33,3 +33,4 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
33
33
|
|
|
34
34
|
return syncTask;
|
|
35
35
|
}
|
|
36
|
+
|
|
@@ -142,11 +142,12 @@ export function syncManagerOnlineFactory(
|
|
|
142
142
|
if (!pollingManager) return;
|
|
143
143
|
|
|
144
144
|
const mySegmentsSyncTask = (pollingManager as IPollingManagerCS).add(matchingKey, readinessManager, storage);
|
|
145
|
+
if (syncEnabled && pushManager) pushManager.add(matchingKey, mySegmentsSyncTask);
|
|
145
146
|
|
|
146
147
|
if (running) {
|
|
147
148
|
if (syncEnabled) {
|
|
148
149
|
if (pushManager) {
|
|
149
|
-
if (pollingManager
|
|
150
|
+
if (pollingManager.isRunning()) {
|
|
150
151
|
// if doing polling, we must start the periodic fetch of data
|
|
151
152
|
if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
|
|
152
153
|
} else {
|
|
@@ -154,7 +155,6 @@ export function syncManagerOnlineFactory(
|
|
|
154
155
|
// of segments since `syncAll` was already executed when starting the main client
|
|
155
156
|
mySegmentsSyncTask.execute();
|
|
156
157
|
}
|
|
157
|
-
pushManager.add(matchingKey, mySegmentsSyncTask);
|
|
158
158
|
} else {
|
|
159
159
|
if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
|
|
160
160
|
}
|
|
@@ -36,7 +36,7 @@ export function eventTrackerFactory(
|
|
|
36
36
|
whenInit(() => {
|
|
37
37
|
// Wrap in a timeout because we don't want it to be blocking.
|
|
38
38
|
setTimeout(() => {
|
|
39
|
-
// copy of event, to avoid unexpected
|
|
39
|
+
// copy of event, to avoid unexpected behavior if modified by integrations
|
|
40
40
|
const eventDataCopy = objectAssign({}, eventData);
|
|
41
41
|
if (properties) eventDataCopy.properties = objectAssign({}, properties);
|
|
42
42
|
// integrationsManager does not throw errors (they are internally handled by each integration module)
|
|
@@ -9,11 +9,16 @@ import SplitIO from '../../types/splitio';
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Impressions tracker stores impressions in cache and pass them to the listener and integrations manager if provided.
|
|
12
|
+
*
|
|
13
|
+
* @param impressionsCache - cache to save impressions
|
|
14
|
+
* @param metadata - runtime metadata (ip, hostname and version)
|
|
15
|
+
* @param impressionListener - optional impression listener
|
|
16
|
+
* @param integrationsManager - optional integrations manager
|
|
17
|
+
* @param strategy - strategy for impressions tracking.
|
|
12
18
|
*/
|
|
13
19
|
export function impressionsTrackerFactory(
|
|
14
20
|
settings: ISettings,
|
|
15
21
|
impressionsCache: IImpressionsCacheBase,
|
|
16
|
-
noneStrategy: IStrategy,
|
|
17
22
|
strategy: IStrategy,
|
|
18
23
|
whenInit: (cb: () => void) => void,
|
|
19
24
|
integrationsManager?: IImpressionsHandler,
|
|
@@ -23,44 +28,40 @@ export function impressionsTrackerFactory(
|
|
|
23
28
|
const { log, impressionListener, runtime: { ip, hostname }, version } = settings;
|
|
24
29
|
|
|
25
30
|
return {
|
|
26
|
-
track(impressions:
|
|
31
|
+
track(impressions: SplitIO.ImpressionDTO[], attributes?: SplitIO.Attributes) {
|
|
27
32
|
if (settings.userConsent === CONSENT_DECLINED) return;
|
|
28
33
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
noneStrategy.process(impression) :
|
|
32
|
-
strategy.process(impression);
|
|
33
|
-
});
|
|
34
|
+
const impressionsCount = impressions.length;
|
|
35
|
+
const { impressionsToStore, impressionsToListener, deduped } = strategy.process(impressions);
|
|
34
36
|
|
|
35
|
-
const
|
|
36
|
-
const impressionsToStoreLength = impressionsToStore.length;
|
|
37
|
+
const impressionsToListenerCount = impressionsToListener.length;
|
|
37
38
|
|
|
38
|
-
if (
|
|
39
|
-
const res = impressionsCache.track(impressionsToStore
|
|
39
|
+
if (impressionsToStore.length > 0) {
|
|
40
|
+
const res = impressionsCache.track(impressionsToStore);
|
|
40
41
|
|
|
41
42
|
// If we're on an async storage, handle error and log it.
|
|
42
43
|
if (thenable(res)) {
|
|
43
44
|
res.then(() => {
|
|
44
|
-
log.info(IMPRESSIONS_TRACKER_SUCCESS, [
|
|
45
|
+
log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
|
|
45
46
|
}).catch(err => {
|
|
46
|
-
log.error(ERROR_IMPRESSIONS_TRACKER, [
|
|
47
|
+
log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
|
|
47
48
|
});
|
|
48
49
|
} else {
|
|
49
50
|
// Record when impressionsCache is sync only (standalone mode)
|
|
50
51
|
// @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
|
|
51
52
|
if (telemetryCache) {
|
|
52
|
-
(telemetryCache as ITelemetryCacheSync).recordImpressionStats(QUEUED,
|
|
53
|
-
(telemetryCache as ITelemetryCacheSync).recordImpressionStats(DEDUPED,
|
|
53
|
+
(telemetryCache as ITelemetryCacheSync).recordImpressionStats(QUEUED, impressionsToStore.length);
|
|
54
|
+
(telemetryCache as ITelemetryCacheSync).recordImpressionStats(DEDUPED, deduped);
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
// @TODO next block might be handled by the integration manager. In that case, the metadata object doesn't need to be passed in the constructor
|
|
59
60
|
if (impressionListener || integrationsManager) {
|
|
60
|
-
for (let i = 0; i <
|
|
61
|
+
for (let i = 0; i < impressionsToListenerCount; i++) {
|
|
61
62
|
const impressionData: SplitIO.ImpressionData = {
|
|
62
|
-
// copy of impression, to avoid unexpected
|
|
63
|
-
impression: objectAssign({},
|
|
63
|
+
// copy of impression, to avoid unexpected behavior if modified by integrations or impressionListener
|
|
64
|
+
impression: objectAssign({}, impressionsToListener[i]),
|
|
64
65
|
attributes,
|
|
65
66
|
ip,
|
|
66
67
|
hostname,
|
|
@@ -6,16 +6,23 @@ import { IStrategy } from '../types';
|
|
|
6
6
|
* Debug strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
|
|
7
7
|
*
|
|
8
8
|
* @param impressionsObserver - impression observer. Previous time (pt property) is included in impression instances
|
|
9
|
-
* @returns
|
|
9
|
+
* @returns IStrategyResult
|
|
10
10
|
*/
|
|
11
11
|
export function strategyDebugFactory(
|
|
12
12
|
impressionsObserver: IImpressionObserver
|
|
13
13
|
): IStrategy {
|
|
14
14
|
|
|
15
15
|
return {
|
|
16
|
-
process(
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
process(impressions: SplitIO.ImpressionDTO[]) {
|
|
17
|
+
impressions.forEach((impression) => {
|
|
18
|
+
// Adds previous time if it is enabled
|
|
19
|
+
impression.pt = impressionsObserver.testAndSet(impression);
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
impressionsToStore: impressions,
|
|
23
|
+
impressionsToListener: impressions,
|
|
24
|
+
deduped: 0
|
|
25
|
+
};
|
|
19
26
|
}
|
|
20
27
|
};
|
|
21
28
|
}
|
|
@@ -5,24 +5,30 @@ import { IStrategy, IUniqueKeysTracker } from '../types';
|
|
|
5
5
|
/**
|
|
6
6
|
* None strategy for impressions tracker.
|
|
7
7
|
*
|
|
8
|
-
* @param
|
|
8
|
+
* @param impressionsCounter - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
|
|
9
9
|
* @param uniqueKeysTracker - unique keys tracker in charge of tracking the unique keys per split.
|
|
10
|
-
* @returns
|
|
10
|
+
* @returns IStrategyResult
|
|
11
11
|
*/
|
|
12
12
|
export function strategyNoneFactory(
|
|
13
|
-
|
|
13
|
+
impressionsCounter: IImpressionCountsCacheBase,
|
|
14
14
|
uniqueKeysTracker: IUniqueKeysTracker
|
|
15
15
|
): IStrategy {
|
|
16
16
|
|
|
17
17
|
return {
|
|
18
|
-
process(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
process(impressions: SplitIO.ImpressionDTO[]) {
|
|
19
|
+
impressions.forEach((impression) => {
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
// Increments impression counter per featureName
|
|
22
|
+
impressionsCounter.track(impression.feature, now, 1);
|
|
23
|
+
// Keep track by unique key
|
|
24
|
+
uniqueKeysTracker.track(impression.keyName, impression.feature);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
impressionsToStore: [],
|
|
29
|
+
impressionsToListener: impressions,
|
|
30
|
+
deduped: 0
|
|
31
|
+
};
|
|
26
32
|
}
|
|
27
33
|
};
|
|
28
34
|
}
|
|
@@ -8,25 +8,35 @@ import { IStrategy } from '../types';
|
|
|
8
8
|
* Optimized strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
|
|
9
9
|
*
|
|
10
10
|
* @param impressionsObserver - impression observer. previous time (pt property) is included in impression instances
|
|
11
|
-
* @param
|
|
12
|
-
* @returns
|
|
11
|
+
* @param impressionsCounter - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
|
|
12
|
+
* @returns IStrategyResult
|
|
13
13
|
*/
|
|
14
14
|
export function strategyOptimizedFactory(
|
|
15
15
|
impressionsObserver: IImpressionObserver,
|
|
16
|
-
|
|
16
|
+
impressionsCounter: IImpressionCountsCacheBase,
|
|
17
17
|
): IStrategy {
|
|
18
18
|
|
|
19
19
|
return {
|
|
20
|
-
process(
|
|
21
|
-
|
|
20
|
+
process(impressions: SplitIO.ImpressionDTO[]) {
|
|
21
|
+
const impressionsToStore: SplitIO.ImpressionDTO[] = [];
|
|
22
|
+
impressions.forEach((impression) => {
|
|
23
|
+
impression.pt = impressionsObserver.testAndSet(impression);
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
const now = Date.now();
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
// Increments impression counter per featureName
|
|
28
|
+
if (impression.pt) impressionsCounter.track(impression.feature, now, 1);
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
// Checks if the impression should be added in queue to be sent
|
|
31
|
+
if (!impression.pt || impression.pt < truncateTimeFrame(now)) {
|
|
32
|
+
impressionsToStore.push(impression);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
impressionsToStore: impressionsToStore,
|
|
37
|
+
impressionsToListener: impressions,
|
|
38
|
+
deduped: impressions.length - impressionsToStore.length
|
|
39
|
+
};
|
|
30
40
|
}
|
|
31
41
|
};
|
|
32
42
|
}
|
package/src/trackers/types.ts
CHANGED
|
@@ -18,7 +18,7 @@ export interface IImpressionsHandler {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface IImpressionsTracker {
|
|
21
|
-
track(impressions:
|
|
21
|
+
track(impressions: SplitIO.ImpressionDTO[], attributes?: SplitIO.Attributes): void
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/** Telemetry tracker */
|
|
@@ -70,6 +70,12 @@ export interface IUniqueKeysTracker {
|
|
|
70
70
|
track(key: string, featureName: string): void;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export interface IStrategyResult {
|
|
74
|
+
impressionsToStore: SplitIO.ImpressionDTO[],
|
|
75
|
+
impressionsToListener: SplitIO.ImpressionDTO[],
|
|
76
|
+
deduped: number
|
|
77
|
+
}
|
|
78
|
+
|
|
73
79
|
export interface IStrategy {
|
|
74
|
-
process(
|
|
80
|
+
process(impressions: SplitIO.ImpressionDTO[]): IStrategyResult
|
|
75
81
|
}
|
|
@@ -159,7 +159,7 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
|
|
|
159
159
|
if (withDefaults.mode === LOCALHOST_MODE && maybeKey === undefined) {
|
|
160
160
|
withDefaults.core.key = 'localhost_key';
|
|
161
161
|
} else {
|
|
162
|
-
// Keeping same
|
|
162
|
+
// Keeping same behavior than JS SDK: if settings key or TT are invalid,
|
|
163
163
|
// `false` value is used as bound key/TT of the default client, which leads to some issues.
|
|
164
164
|
// @ts-ignore, @TODO handle invalid keys as a non-recoverable error?
|
|
165
165
|
withDefaults.core.key = validateKey(log, maybeKey, LOG_PREFIX_CLIENT_INSTANTIATION);
|
package/types/index.d.ts
CHANGED
package/types/splitio.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Type definitions for Split Software SDKs
|
|
2
|
-
// Project:
|
|
2
|
+
// Project: https://www.split.io/
|
|
3
3
|
|
|
4
4
|
import { RedisOptions } from 'ioredis';
|
|
5
5
|
import { RequestOptions } from 'http';
|
|
@@ -862,10 +862,6 @@ declare namespace SplitIO {
|
|
|
862
862
|
* The default treatment of the feature flag.
|
|
863
863
|
*/
|
|
864
864
|
defaultTreatment: string;
|
|
865
|
-
/**
|
|
866
|
-
* Whether the feature flag has impressions tracking enabled or not.
|
|
867
|
-
*/
|
|
868
|
-
trackImpressions: boolean;
|
|
869
865
|
};
|
|
870
866
|
/**
|
|
871
867
|
* A promise that resolves to a feature flag view or null if the feature flag is not found.
|