@splitsoftware/splitio-commons 2.1.1-rc.0 → 2.1.1-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 +7 -0
- package/README.md +0 -1
- package/cjs/evaluator/combiners/and.js +6 -2
- package/cjs/evaluator/combiners/ifelseif.js +6 -6
- package/cjs/evaluator/condition/index.js +5 -6
- package/cjs/evaluator/index.js +7 -7
- package/cjs/evaluator/matchers/index.js +1 -3
- package/cjs/evaluator/matchers/matcherTypes.js +0 -1
- package/cjs/evaluator/matchersTransform/index.js +0 -4
- package/cjs/evaluator/parser/index.js +2 -2
- package/cjs/evaluator/value/sanitize.js +0 -1
- package/cjs/logger/constants.js +3 -4
- package/cjs/logger/messages/debug.js +2 -3
- package/cjs/logger/messages/error.js +1 -1
- package/cjs/logger/messages/warn.js +2 -2
- package/cjs/readiness/readinessManager.js +6 -0
- package/cjs/sdkClient/client.js +29 -19
- package/cjs/sdkClient/clientAttributesDecoration.js +19 -25
- package/cjs/sdkClient/clientInputValidation.js +28 -26
- package/cjs/services/splitApi.js +2 -2
- package/cjs/storages/AbstractSplitsCacheAsync.js +0 -7
- package/cjs/storages/AbstractSplitsCacheSync.js +2 -12
- package/cjs/storages/KeyBuilder.js +0 -9
- package/cjs/storages/KeyBuilderCS.js +4 -4
- package/cjs/storages/KeyBuilderSS.js +0 -3
- package/cjs/storages/dataLoader.js +3 -2
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +23 -76
- package/cjs/storages/inLocalStorage/index.js +5 -7
- package/cjs/storages/inLocalStorage/validateCache.js +79 -0
- package/cjs/storages/inMemory/InMemoryStorage.js +0 -3
- package/cjs/storages/inMemory/InMemoryStorageCS.js +0 -4
- package/cjs/storages/inRedis/index.js +0 -2
- package/cjs/storages/pluggable/index.js +2 -3
- package/cjs/storages/utils.js +1 -0
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +2 -2
- package/cjs/sync/polling/pollingManagerCS.js +7 -7
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +33 -60
- package/cjs/sync/streaming/SSEHandler/index.js +0 -1
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +77 -106
- package/cjs/sync/streaming/constants.js +1 -2
- package/cjs/sync/streaming/pushManager.js +16 -3
- package/cjs/sync/submitters/impressionsSubmitter.js +3 -2
- package/cjs/sync/syncManagerOnline.js +10 -5
- package/cjs/trackers/strategy/strategyDebug.js +2 -0
- package/cjs/trackers/strategy/strategyOptimized.js +3 -0
- package/cjs/utils/constants/index.js +2 -3
- package/cjs/utils/inputValidation/eventProperties.js +12 -1
- package/cjs/utils/inputValidation/index.js +3 -1
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/evaluator/combiners/and.js +6 -2
- package/esm/evaluator/combiners/ifelseif.js +7 -7
- package/esm/evaluator/condition/index.js +5 -6
- package/esm/evaluator/index.js +7 -7
- package/esm/evaluator/matchers/index.js +1 -3
- package/esm/evaluator/matchers/matcherTypes.js +0 -1
- package/esm/evaluator/matchersTransform/index.js +0 -4
- package/esm/evaluator/parser/index.js +2 -2
- package/esm/evaluator/value/sanitize.js +0 -1
- package/esm/logger/constants.js +0 -1
- package/esm/logger/messages/debug.js +2 -3
- package/esm/logger/messages/error.js +1 -1
- package/esm/logger/messages/warn.js +2 -2
- package/esm/readiness/readinessManager.js +6 -0
- package/esm/sdkClient/client.js +29 -19
- package/esm/sdkClient/clientAttributesDecoration.js +19 -25
- package/esm/sdkClient/clientInputValidation.js +29 -27
- package/esm/services/splitApi.js +2 -2
- package/esm/storages/AbstractSplitsCacheAsync.js +0 -7
- package/esm/storages/AbstractSplitsCacheSync.js +2 -12
- package/esm/storages/KeyBuilder.js +0 -9
- package/esm/storages/KeyBuilderCS.js +4 -4
- package/esm/storages/KeyBuilderSS.js +0 -3
- package/esm/storages/dataLoader.js +2 -1
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +23 -76
- package/esm/storages/inLocalStorage/index.js +5 -7
- package/esm/storages/inLocalStorage/validateCache.js +75 -0
- package/esm/storages/inMemory/InMemoryStorage.js +0 -3
- package/esm/storages/inMemory/InMemoryStorageCS.js +0 -4
- package/esm/storages/inRedis/index.js +0 -2
- package/esm/storages/pluggable/index.js +2 -3
- package/esm/storages/utils.js +1 -0
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +2 -2
- package/esm/sync/polling/pollingManagerCS.js +7 -7
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/esm/sync/polling/updaters/splitChangesUpdater.js +34 -61
- package/esm/sync/streaming/SSEHandler/index.js +1 -2
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +73 -102
- package/esm/sync/streaming/constants.js +0 -1
- package/esm/sync/streaming/pushManager.js +19 -6
- package/esm/sync/submitters/impressionsSubmitter.js +3 -2
- package/esm/sync/syncManagerOnline.js +10 -5
- package/esm/trackers/strategy/strategyDebug.js +2 -0
- package/esm/trackers/strategy/strategyOptimized.js +3 -0
- package/esm/utils/constants/index.js +1 -2
- package/esm/utils/inputValidation/eventProperties.js +10 -0
- package/esm/utils/inputValidation/index.js +1 -0
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/dtos/types.ts +8 -32
- package/src/evaluator/Engine.ts +1 -1
- package/src/evaluator/combiners/and.ts +4 -5
- package/src/evaluator/combiners/ifelseif.ts +9 -7
- package/src/evaluator/condition/engineUtils.ts +1 -1
- package/src/evaluator/condition/index.ts +12 -12
- package/src/evaluator/index.ts +7 -7
- package/src/evaluator/matchers/index.ts +1 -3
- package/src/evaluator/matchers/matcherTypes.ts +0 -1
- package/src/evaluator/matchersTransform/index.ts +0 -3
- package/src/evaluator/parser/index.ts +3 -3
- package/src/evaluator/types.ts +2 -2
- package/src/evaluator/value/index.ts +2 -2
- package/src/evaluator/value/sanitize.ts +4 -5
- package/src/logger/constants.ts +0 -1
- package/src/logger/messages/debug.ts +2 -3
- package/src/logger/messages/error.ts +1 -1
- package/src/logger/messages/warn.ts +2 -2
- package/src/readiness/readinessManager.ts +5 -0
- package/src/sdkClient/client.ts +31 -21
- package/src/sdkClient/clientAttributesDecoration.ts +20 -27
- package/src/sdkClient/clientInputValidation.ts +30 -27
- package/src/sdkManager/index.ts +1 -1
- package/src/services/splitApi.ts +2 -2
- package/src/services/types.ts +1 -1
- package/src/storages/AbstractSplitsCacheAsync.ts +0 -8
- package/src/storages/AbstractSplitsCacheSync.ts +3 -14
- package/src/storages/KeyBuilder.ts +0 -12
- package/src/storages/KeyBuilderCS.ts +5 -5
- package/src/storages/KeyBuilderSS.ts +0 -4
- package/src/storages/dataLoader.ts +3 -1
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +26 -87
- package/src/storages/inLocalStorage/index.ts +8 -12
- package/src/storages/inLocalStorage/validateCache.ts +91 -0
- package/src/storages/inMemory/InMemoryStorage.ts +0 -3
- package/src/storages/inMemory/InMemoryStorageCS.ts +0 -4
- package/src/storages/inRedis/index.ts +0 -2
- package/src/storages/pluggable/index.ts +2 -3
- package/src/storages/types.ts +2 -37
- package/src/storages/utils.ts +1 -0
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +6 -5
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +1 -2
- package/src/sync/polling/fetchers/types.ts +0 -1
- package/src/sync/polling/pollingManagerCS.ts +7 -7
- package/src/sync/polling/types.ts +2 -2
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -2
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
- package/src/sync/polling/updaters/splitChangesUpdater.ts +43 -71
- package/src/sync/streaming/SSEHandler/index.ts +1 -2
- package/src/sync/streaming/SSEHandler/types.ts +2 -2
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +68 -98
- package/src/sync/streaming/constants.ts +0 -1
- package/src/sync/streaming/parseUtils.ts +2 -2
- package/src/sync/streaming/pushManager.ts +18 -6
- package/src/sync/streaming/types.ts +2 -3
- package/src/sync/submitters/impressionsSubmitter.ts +3 -2
- package/src/sync/submitters/types.ts +23 -33
- package/src/sync/syncManagerOnline.ts +11 -5
- package/src/trackers/strategy/strategyDebug.ts +2 -0
- package/src/trackers/strategy/strategyOptimized.ts +3 -0
- package/src/utils/constants/index.ts +1 -2
- package/src/utils/inputValidation/eventProperties.ts +10 -0
- package/src/utils/inputValidation/index.ts +1 -0
- package/src/utils/lang/index.ts +2 -2
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/splitio.d.ts +128 -36
- package/cjs/evaluator/matchers/rbsegment.js +0 -43
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -117
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +0 -61
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +0 -64
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +0 -64
- package/cjs/utils/constants/browser.js +0 -5
- package/esm/evaluator/matchers/rbsegment.js +0 -39
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -114
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +0 -58
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +0 -61
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +0 -61
- package/esm/utils/constants/browser.js +0 -2
- package/src/evaluator/matchers/rbsegment.ts +0 -61
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +0 -136
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +0 -68
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +0 -79
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +0 -76
- package/src/utils/constants/browser.ts +0 -2
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { ISettings } from '../../types';
|
|
2
|
+
import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
|
|
3
|
+
import { getStorageHash } from '../KeyBuilder';
|
|
4
|
+
import { LOG_PREFIX } from './constants';
|
|
5
|
+
import type { SplitsCacheInLocal } from './SplitsCacheInLocal';
|
|
6
|
+
import type { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
|
|
7
|
+
import { KeyBuilderCS } from '../KeyBuilderCS';
|
|
8
|
+
import SplitIO from '../../../types/splitio';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_CACHE_EXPIRATION_IN_DAYS = 10;
|
|
11
|
+
const MILLIS_IN_A_DAY = 86400000;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Validates if cache should be cleared and sets the cache `hash` if needed.
|
|
15
|
+
*
|
|
16
|
+
* @returns `true` if cache should be cleared, `false` otherwise
|
|
17
|
+
*/
|
|
18
|
+
function validateExpiration(options: SplitIO.InLocalStorageOptions, settings: ISettings, keys: KeyBuilderCS, currentTimestamp: number, isThereCache: boolean) {
|
|
19
|
+
const { log } = settings;
|
|
20
|
+
|
|
21
|
+
// Check expiration
|
|
22
|
+
const lastUpdatedTimestamp = parseInt(localStorage.getItem(keys.buildLastUpdatedKey()) as string, 10);
|
|
23
|
+
if (!isNaNNumber(lastUpdatedTimestamp)) {
|
|
24
|
+
const cacheExpirationInDays = isFiniteNumber(options.expirationDays) && options.expirationDays >= 1 ? options.expirationDays : DEFAULT_CACHE_EXPIRATION_IN_DAYS;
|
|
25
|
+
const expirationTimestamp = currentTimestamp - MILLIS_IN_A_DAY * cacheExpirationInDays;
|
|
26
|
+
if (lastUpdatedTimestamp < expirationTimestamp) {
|
|
27
|
+
log.info(LOG_PREFIX + 'Cache expired more than ' + cacheExpirationInDays + ' days ago. Cleaning up cache');
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check hash
|
|
33
|
+
const storageHashKey = keys.buildHashKey();
|
|
34
|
+
const storageHash = localStorage.getItem(storageHashKey);
|
|
35
|
+
const currentStorageHash = getStorageHash(settings);
|
|
36
|
+
|
|
37
|
+
if (storageHash !== currentStorageHash) {
|
|
38
|
+
try {
|
|
39
|
+
localStorage.setItem(storageHashKey, currentStorageHash);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
log.error(LOG_PREFIX + e);
|
|
42
|
+
}
|
|
43
|
+
if (isThereCache) {
|
|
44
|
+
log.info(LOG_PREFIX + 'SDK key, flags filter criteria, or flags spec version has changed. Cleaning up cache');
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false; // No cache to clear
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Clear on init
|
|
51
|
+
if (options.clearOnInit) {
|
|
52
|
+
const lastClearTimestamp = parseInt(localStorage.getItem(keys.buildLastClear()) as string, 10);
|
|
53
|
+
|
|
54
|
+
if (isNaNNumber(lastClearTimestamp) || lastClearTimestamp < currentTimestamp - MILLIS_IN_A_DAY) {
|
|
55
|
+
log.info(LOG_PREFIX + 'clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache');
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Clean cache if:
|
|
63
|
+
* - it has expired, i.e., its `lastUpdated` timestamp is older than the given `expirationTimestamp`
|
|
64
|
+
* - its hash has changed, i.e., the SDK key, flags filter criteria or flags spec version was modified
|
|
65
|
+
* - `clearOnInit` was set and cache was not cleared in the last 24 hours
|
|
66
|
+
*
|
|
67
|
+
* @returns `true` if cache is ready to be used, `false` otherwise (cache was cleared or there is no cache)
|
|
68
|
+
*/
|
|
69
|
+
export function validateCache(options: SplitIO.InLocalStorageOptions, settings: ISettings, keys: KeyBuilderCS, splits: SplitsCacheInLocal, segments: MySegmentsCacheInLocal, largeSegments: MySegmentsCacheInLocal): boolean {
|
|
70
|
+
|
|
71
|
+
const currentTimestamp = Date.now();
|
|
72
|
+
const isThereCache = splits.getChangeNumber() > -1;
|
|
73
|
+
|
|
74
|
+
if (validateExpiration(options, settings, keys, currentTimestamp, isThereCache)) {
|
|
75
|
+
splits.clear();
|
|
76
|
+
segments.clear();
|
|
77
|
+
largeSegments.clear();
|
|
78
|
+
|
|
79
|
+
// Update last clear timestamp
|
|
80
|
+
try {
|
|
81
|
+
localStorage.setItem(keys.buildLastClear(), currentTimestamp + '');
|
|
82
|
+
} catch (e) {
|
|
83
|
+
settings.log.error(LOG_PREFIX + e);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Check if ready from cache
|
|
90
|
+
return isThereCache;
|
|
91
|
+
}
|
|
@@ -7,7 +7,6 @@ import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
|
7
7
|
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
9
9
|
import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
|
|
10
|
-
import { RBSegmentsCacheInMemory } from './RBSegmentsCacheInMemory';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* InMemory storage factory for standalone server-side SplitFactory
|
|
@@ -18,12 +17,10 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
|
|
|
18
17
|
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { __splitFiltersValidation } } } = params;
|
|
19
18
|
|
|
20
19
|
const splits = new SplitsCacheInMemory(__splitFiltersValidation);
|
|
21
|
-
const rbSegments = new RBSegmentsCacheInMemory();
|
|
22
20
|
const segments = new SegmentsCacheInMemory();
|
|
23
21
|
|
|
24
22
|
const storage = {
|
|
25
23
|
splits,
|
|
26
|
-
rbSegments,
|
|
27
24
|
segments,
|
|
28
25
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
29
26
|
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
@@ -7,7 +7,6 @@ import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
|
7
7
|
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
9
9
|
import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
|
|
10
|
-
import { RBSegmentsCacheInMemory } from './RBSegmentsCacheInMemory';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* InMemory storage factory for standalone client-side SplitFactory
|
|
@@ -18,13 +17,11 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
18
17
|
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize }, sync: { __splitFiltersValidation } } } = params;
|
|
19
18
|
|
|
20
19
|
const splits = new SplitsCacheInMemory(__splitFiltersValidation);
|
|
21
|
-
const rbSegments = new RBSegmentsCacheInMemory();
|
|
22
20
|
const segments = new MySegmentsCacheInMemory();
|
|
23
21
|
const largeSegments = new MySegmentsCacheInMemory();
|
|
24
22
|
|
|
25
23
|
const storage = {
|
|
26
24
|
splits,
|
|
27
|
-
rbSegments,
|
|
28
25
|
segments,
|
|
29
26
|
largeSegments,
|
|
30
27
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
@@ -39,7 +36,6 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
39
36
|
shared() {
|
|
40
37
|
return {
|
|
41
38
|
splits: this.splits,
|
|
42
|
-
rbSegments: this.rbSegments,
|
|
43
39
|
segments: new MySegmentsCacheInMemory(),
|
|
44
40
|
largeSegments: new MySegmentsCacheInMemory(),
|
|
45
41
|
impressions: this.impressions,
|
|
@@ -11,7 +11,6 @@ import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
|
|
|
11
11
|
import { UniqueKeysCacheInRedis } from './UniqueKeysCacheInRedis';
|
|
12
12
|
import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
|
|
13
13
|
import { metadataBuilder } from '../utils';
|
|
14
|
-
import { RBSegmentsCacheInRedis } from './RBSegmentsCacheInRedis';
|
|
15
14
|
|
|
16
15
|
export interface InRedisStorageOptions {
|
|
17
16
|
prefix?: string
|
|
@@ -51,7 +50,6 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
|
|
|
51
50
|
|
|
52
51
|
return {
|
|
53
52
|
splits: new SplitsCacheInRedis(log, keys, redisClient, settings.sync.__splitFiltersValidation),
|
|
54
|
-
rbSegments: new RBSegmentsCacheInRedis(log, keys, redisClient),
|
|
55
53
|
segments: new SegmentsCacheInRedis(log, keys, redisClient),
|
|
56
54
|
impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
|
|
57
55
|
impressionCounts: impressionCountsCache,
|
|
@@ -20,7 +20,6 @@ import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
|
|
|
20
20
|
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
|
|
21
21
|
import { metadataBuilder } from '../utils';
|
|
22
22
|
import { LOG_PREFIX } from '../pluggable/constants';
|
|
23
|
-
import { RBSegmentsCachePluggable } from './RBSegmentsCachePluggable';
|
|
24
23
|
|
|
25
24
|
const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
|
|
26
25
|
const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
|
|
@@ -89,7 +88,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
89
88
|
// Connects to wrapper and emits SDK_READY event on main client
|
|
90
89
|
const connectPromise = wrapper.connect().then(() => {
|
|
91
90
|
if (isSynchronizer) {
|
|
92
|
-
//
|
|
91
|
+
// @TODO reuse InLocalStorage::validateCache logic
|
|
92
|
+
// In standalone or producer mode, clear storage if SDK key, flags filter criteria or flags spec version was modified
|
|
93
93
|
return wrapper.get(keys.buildHashKey()).then((hash) => {
|
|
94
94
|
const currentHash = getStorageHash(settings);
|
|
95
95
|
if (hash !== currentHash) {
|
|
@@ -117,7 +117,6 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
117
117
|
|
|
118
118
|
return {
|
|
119
119
|
splits: new SplitsCachePluggable(log, keys, wrapper, settings.sync.__splitFiltersValidation),
|
|
120
|
-
rbSegments: new RBSegmentsCachePluggable(log, keys, wrapper),
|
|
121
120
|
segments: new SegmentsCachePluggable(log, keys, wrapper),
|
|
122
121
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
123
122
|
impressionCounts: impressionCountsCache,
|
package/src/storages/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import SplitIO from '../../types/splitio';
|
|
2
|
-
import { MaybeThenable, ISplit,
|
|
2
|
+
import { MaybeThenable, ISplit, IMySegmentsResponse } from '../dtos/types';
|
|
3
3
|
import { MySegmentsData } from '../sync/polling/types';
|
|
4
4
|
import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs, TelemetryUsageStatsPayload, UpdatesFromSSEEnum } from '../sync/submitters/types';
|
|
5
5
|
import { ISettings } from '../types';
|
|
@@ -189,8 +189,6 @@ export interface ISplitsCacheBase {
|
|
|
189
189
|
// only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === -1) or contains a FF using segments or large segments
|
|
190
190
|
usesSegments(): MaybeThenable<boolean>,
|
|
191
191
|
clear(): MaybeThenable<boolean | void>,
|
|
192
|
-
// should never reject or throw an exception. Instead return false by default, to avoid emitting SDK_READY_FROM_CACHE.
|
|
193
|
-
checkCache(): MaybeThenable<boolean>,
|
|
194
192
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): MaybeThenable<boolean>,
|
|
195
193
|
getNamesByFlagSets(flagSets: string[]): MaybeThenable<Set<string>[]>
|
|
196
194
|
}
|
|
@@ -205,7 +203,6 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
205
203
|
trafficTypeExists(trafficType: string): boolean,
|
|
206
204
|
usesSegments(): boolean,
|
|
207
205
|
clear(): void,
|
|
208
|
-
checkCache(): boolean,
|
|
209
206
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
|
|
210
207
|
getNamesByFlagSets(flagSets: string[]): Set<string>[]
|
|
211
208
|
}
|
|
@@ -220,39 +217,10 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
|
220
217
|
trafficTypeExists(trafficType: string): Promise<boolean>,
|
|
221
218
|
usesSegments(): Promise<boolean>,
|
|
222
219
|
clear(): Promise<boolean | void>,
|
|
223
|
-
checkCache(): Promise<boolean>,
|
|
224
220
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
|
|
225
221
|
getNamesByFlagSets(flagSets: string[]): Promise<Set<string>[]>
|
|
226
222
|
}
|
|
227
223
|
|
|
228
|
-
/** Rule-Based Segments cache */
|
|
229
|
-
|
|
230
|
-
export interface IRBSegmentsCacheBase {
|
|
231
|
-
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): MaybeThenable<boolean>,
|
|
232
|
-
get(name: string): MaybeThenable<IRBSegment | null>,
|
|
233
|
-
getChangeNumber(): MaybeThenable<number>,
|
|
234
|
-
clear(): MaybeThenable<boolean | void>,
|
|
235
|
-
contains(names: Set<string>): MaybeThenable<boolean>,
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export interface IRBSegmentsCacheSync extends IRBSegmentsCacheBase {
|
|
239
|
-
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean,
|
|
240
|
-
get(name: string): IRBSegment | null,
|
|
241
|
-
getChangeNumber(): number,
|
|
242
|
-
clear(): void,
|
|
243
|
-
contains(names: Set<string>): boolean,
|
|
244
|
-
// Used only for smart pausing in client-side standalone. Returns true if the storage contains a RBSegment using segments or large segments matchers
|
|
245
|
-
usesSegments(): boolean,
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
export interface IRBSegmentsCacheAsync extends IRBSegmentsCacheBase {
|
|
249
|
-
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): Promise<boolean>,
|
|
250
|
-
get(name: string): Promise<IRBSegment | null>,
|
|
251
|
-
getChangeNumber(): Promise<number>,
|
|
252
|
-
clear(): Promise<boolean | void>,
|
|
253
|
-
contains(names: Set<string>): Promise<boolean>,
|
|
254
|
-
}
|
|
255
|
-
|
|
256
224
|
/** Segments cache */
|
|
257
225
|
|
|
258
226
|
export interface ISegmentsCacheBase {
|
|
@@ -451,7 +419,6 @@ export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync,
|
|
|
451
419
|
|
|
452
420
|
export interface IStorageBase<
|
|
453
421
|
TSplitsCache extends ISplitsCacheBase = ISplitsCacheBase,
|
|
454
|
-
TRBSegmentsCache extends IRBSegmentsCacheBase = IRBSegmentsCacheBase,
|
|
455
422
|
TSegmentsCache extends ISegmentsCacheBase = ISegmentsCacheBase,
|
|
456
423
|
TImpressionsCache extends IImpressionsCacheBase = IImpressionsCacheBase,
|
|
457
424
|
TImpressionsCountCache extends IImpressionCountsCacheBase = IImpressionCountsCacheBase,
|
|
@@ -460,7 +427,6 @@ export interface IStorageBase<
|
|
|
460
427
|
TUniqueKeysCache extends IUniqueKeysCacheBase = IUniqueKeysCacheBase
|
|
461
428
|
> {
|
|
462
429
|
splits: TSplitsCache,
|
|
463
|
-
rbSegments: TRBSegmentsCache,
|
|
464
430
|
segments: TSegmentsCache,
|
|
465
431
|
impressions: TImpressionsCache,
|
|
466
432
|
impressionCounts: TImpressionsCountCache,
|
|
@@ -473,7 +439,6 @@ export interface IStorageBase<
|
|
|
473
439
|
|
|
474
440
|
export interface IStorageSync extends IStorageBase<
|
|
475
441
|
ISplitsCacheSync,
|
|
476
|
-
IRBSegmentsCacheSync,
|
|
477
442
|
ISegmentsCacheSync,
|
|
478
443
|
IImpressionsCacheSync,
|
|
479
444
|
IImpressionCountsCacheSync,
|
|
@@ -482,12 +447,12 @@ export interface IStorageSync extends IStorageBase<
|
|
|
482
447
|
IUniqueKeysCacheSync
|
|
483
448
|
> {
|
|
484
449
|
// Defined in client-side
|
|
450
|
+
validateCache?: () => boolean, // @TODO support async
|
|
485
451
|
largeSegments?: ISegmentsCacheSync,
|
|
486
452
|
}
|
|
487
453
|
|
|
488
454
|
export interface IStorageAsync extends IStorageBase<
|
|
489
455
|
ISplitsCacheAsync,
|
|
490
|
-
IRBSegmentsCacheAsync,
|
|
491
456
|
ISegmentsCacheAsync,
|
|
492
457
|
IImpressionsCacheAsync | IImpressionsCacheSync,
|
|
493
458
|
IImpressionCountsCacheBase,
|
package/src/storages/utils.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { forOwn } from '../../../utils/lang';
|
|
2
2
|
import { IReadinessManager } from '../../../readiness/types';
|
|
3
|
-
import {
|
|
3
|
+
import { IStorageSync } from '../../../storages/types';
|
|
4
4
|
import { ISplitsParser } from '../splitsParser/types';
|
|
5
5
|
import { ISplit, ISplitPartial } from '../../../dtos/types';
|
|
6
6
|
import { syncTaskFactory } from '../../syncTask';
|
|
@@ -15,7 +15,7 @@ import { SYNC_OFFLINE_DATA, ERROR_SYNC_OFFLINE_LOADING } from '../../../logger/c
|
|
|
15
15
|
*/
|
|
16
16
|
export function fromObjectUpdaterFactory(
|
|
17
17
|
splitsParser: ISplitsParser,
|
|
18
|
-
storage:
|
|
18
|
+
storage: Pick<IStorageSync, 'splits' | 'validateCache'>,
|
|
19
19
|
readiness: IReadinessManager,
|
|
20
20
|
settings: ISettings,
|
|
21
21
|
): () => Promise<boolean> {
|
|
@@ -59,9 +59,10 @@ export function fromObjectUpdaterFactory(
|
|
|
59
59
|
|
|
60
60
|
if (startingUp) {
|
|
61
61
|
startingUp = false;
|
|
62
|
-
|
|
62
|
+
const isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
|
|
63
|
+
Promise.resolve().then(() => {
|
|
63
64
|
// Emits SDK_READY_FROM_CACHE
|
|
64
|
-
if (
|
|
65
|
+
if (isCacheLoaded) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
65
66
|
// Emits SDK_READY
|
|
66
67
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
67
68
|
});
|
|
@@ -79,7 +80,7 @@ export function fromObjectUpdaterFactory(
|
|
|
79
80
|
*/
|
|
80
81
|
export function fromObjectSyncTaskFactory(
|
|
81
82
|
splitsParser: ISplitsParser,
|
|
82
|
-
storage:
|
|
83
|
+
storage: Pick<IStorageSync, 'splits' | 'validateCache'>,
|
|
83
84
|
readiness: IReadinessManager,
|
|
84
85
|
settings: ISettings
|
|
85
86
|
): ISyncTask<[], boolean> {
|
|
@@ -11,12 +11,11 @@ export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges
|
|
|
11
11
|
since: number,
|
|
12
12
|
noCache?: boolean,
|
|
13
13
|
till?: number,
|
|
14
|
-
rbSince?: number,
|
|
15
14
|
// Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
|
|
16
15
|
decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
|
|
17
16
|
) {
|
|
18
17
|
|
|
19
|
-
let splitsPromise = fetchSplitChanges(since, noCache, till
|
|
18
|
+
let splitsPromise = fetchSplitChanges(since, noCache, till);
|
|
20
19
|
if (decorator) splitsPromise = decorator(splitsPromise);
|
|
21
20
|
|
|
22
21
|
return splitsPromise.then(resp => resp.json());
|
|
@@ -43,10 +43,10 @@ export function pollingManagerCSFactory(
|
|
|
43
43
|
// smart pausing
|
|
44
44
|
readiness.splits.on(SDK_SPLITS_ARRIVED, () => {
|
|
45
45
|
if (!splitsSyncTask.isRunning()) return; // noop if not doing polling
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
log.info(POLLING_SMART_PAUSING, [
|
|
49
|
-
if (
|
|
46
|
+
const splitsHaveSegments = storage.splits.usesSegments();
|
|
47
|
+
if (splitsHaveSegments !== mySegmentsSyncTask.isRunning()) {
|
|
48
|
+
log.info(POLLING_SMART_PAUSING, [splitsHaveSegments ? 'ON' : 'OFF']);
|
|
49
|
+
if (splitsHaveSegments) {
|
|
50
50
|
startMySegmentsSyncTasks();
|
|
51
51
|
} else {
|
|
52
52
|
stopMySegmentsSyncTasks();
|
|
@@ -59,9 +59,9 @@ export function pollingManagerCSFactory(
|
|
|
59
59
|
|
|
60
60
|
// smart ready
|
|
61
61
|
function smartReady() {
|
|
62
|
-
if (!readiness.isReady() && !storage.splits.usesSegments()
|
|
62
|
+
if (!readiness.isReady() && !storage.splits.usesSegments()) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
63
63
|
}
|
|
64
|
-
if (!storage.splits.usesSegments()
|
|
64
|
+
if (!storage.splits.usesSegments()) setTimeout(smartReady, 0);
|
|
65
65
|
else readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady);
|
|
66
66
|
|
|
67
67
|
mySegmentsSyncTasks[matchingKey] = mySegmentsSyncTask;
|
|
@@ -77,7 +77,7 @@ export function pollingManagerCSFactory(
|
|
|
77
77
|
log.info(POLLING_START);
|
|
78
78
|
|
|
79
79
|
splitsSyncTask.start();
|
|
80
|
-
if (storage.splits.usesSegments()
|
|
80
|
+
if (storage.splits.usesSegments()) startMySegmentsSyncTasks();
|
|
81
81
|
},
|
|
82
82
|
|
|
83
83
|
// Stop periodic fetching (polling)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ISplit } from '../../dtos/types';
|
|
2
2
|
import { IReadinessManager } from '../../readiness/types';
|
|
3
3
|
import { IStorageSync } from '../../storages/types';
|
|
4
4
|
import { MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../streaming/types';
|
|
5
5
|
import { ITask, ISyncTask } from '../types';
|
|
6
6
|
|
|
7
|
-
export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit
|
|
7
|
+
export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }], boolean> { }
|
|
8
8
|
|
|
9
9
|
export interface ISegmentsSyncTask extends ISyncTask<[fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number], boolean> { }
|
|
10
10
|
|
|
@@ -27,7 +27,7 @@ export function mySegmentsUpdaterFactory(
|
|
|
27
27
|
matchingKey: string
|
|
28
28
|
): IMySegmentsUpdater {
|
|
29
29
|
|
|
30
|
-
const { splits,
|
|
30
|
+
const { splits, segments, largeSegments } = storage;
|
|
31
31
|
let readyOnAlreadyExistentState = true;
|
|
32
32
|
let startingUp = true;
|
|
33
33
|
|
|
@@ -51,7 +51,7 @@ export function mySegmentsUpdaterFactory(
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// Notify update if required
|
|
54
|
-
if (
|
|
54
|
+
if (splits.usesSegments() && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
55
55
|
readyOnAlreadyExistentState = false;
|
|
56
56
|
segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED);
|
|
57
57
|
}
|
|
@@ -51,7 +51,7 @@ export function segmentChangesUpdaterFactory(
|
|
|
51
51
|
* Returned promise will not be rejected.
|
|
52
52
|
*
|
|
53
53
|
* @param fetchOnlyNew - if true, only fetch the segments that not exists, i.e., which `changeNumber` is equal to -1.
|
|
54
|
-
* This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE
|
|
54
|
+
* This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE notifications.
|
|
55
55
|
* @param segmentName - segment name to fetch. By passing `undefined` it fetches the list of segments registered at the storage
|
|
56
56
|
* @param noCache - true to revalidate data to fetch on a SEGMENT_UPDATE notifications.
|
|
57
57
|
* @param till - till target for the provided segmentName, for CDN bypass.
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { ISegmentsCacheBase, IStorageBase } from '../../../storages/types';
|
|
2
2
|
import { ISplitChangesFetcher } from '../fetchers/types';
|
|
3
|
-
import {
|
|
3
|
+
import { ISplit, ISplitChangesResponse, ISplitFiltersValidation } from '../../../dtos/types';
|
|
4
4
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
5
5
|
import { timeout } from '../../../utils/promise/timeout';
|
|
6
|
-
import { SDK_SPLITS_ARRIVED
|
|
6
|
+
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
7
7
|
import { ILogger } from '../../../logger/types';
|
|
8
|
-
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE,
|
|
8
|
+
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
9
9
|
import { startsWith } from '../../../utils/lang';
|
|
10
|
-
import {
|
|
10
|
+
import { IN_SEGMENT } from '../../../utils/constants';
|
|
11
11
|
import { setToArray } from '../../../utils/lang/sets';
|
|
12
|
-
import { SPLIT_UPDATE } from '../../streaming/constants';
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
type SplitChangesUpdater = (noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) => Promise<boolean>
|
|
13
|
+
type ISplitChangesUpdater = (noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }) => Promise<boolean>
|
|
16
14
|
|
|
17
15
|
// Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
|
|
18
16
|
// Returns a promise that could be rejected.
|
|
@@ -29,24 +27,24 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise<boolean> {
|
|
|
29
27
|
* Collect segments from a raw split definition.
|
|
30
28
|
* Exported for testing purposes.
|
|
31
29
|
*/
|
|
32
|
-
export function parseSegments(
|
|
33
|
-
|
|
34
|
-
const segments = new Set<string>(excluded && excluded.segments);
|
|
30
|
+
export function parseSegments({ conditions }: ISplit): Set<string> {
|
|
31
|
+
let segments = new Set<string>();
|
|
35
32
|
|
|
36
33
|
for (let i = 0; i < conditions.length; i++) {
|
|
37
34
|
const matchers = conditions[i].matcherGroup.matchers;
|
|
38
35
|
|
|
39
36
|
matchers.forEach(matcher => {
|
|
40
|
-
if (matcher.matcherType ===
|
|
37
|
+
if (matcher.matcherType === IN_SEGMENT) segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
|
|
41
38
|
});
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
return segments;
|
|
45
42
|
}
|
|
46
43
|
|
|
47
|
-
interface ISplitMutations
|
|
48
|
-
added:
|
|
49
|
-
removed:
|
|
44
|
+
interface ISplitMutations {
|
|
45
|
+
added: ISplit[],
|
|
46
|
+
removed: ISplit[],
|
|
47
|
+
segments: string[]
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
/**
|
|
@@ -75,21 +73,25 @@ function matchFilters(featureFlag: ISplit, filters: ISplitFiltersValidation) {
|
|
|
75
73
|
* i.e., an object with added splits, removed splits and used segments.
|
|
76
74
|
* Exported for testing purposes.
|
|
77
75
|
*/
|
|
78
|
-
export function
|
|
76
|
+
export function computeSplitsMutation(entries: ISplit[], filters: ISplitFiltersValidation): ISplitMutations {
|
|
77
|
+
const segments = new Set<string>();
|
|
78
|
+
const computed = entries.reduce((accum, split) => {
|
|
79
|
+
if (split.status === 'ACTIVE' && matchFilters(split, filters)) {
|
|
80
|
+
accum.added.push(split);
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
if (ruleEntity.status === 'ACTIVE' && (!filters || matchFilters(ruleEntity as ISplit, filters))) {
|
|
82
|
-
accum.added.push(ruleEntity);
|
|
83
|
-
|
|
84
|
-
parseSegments(ruleEntity).forEach((segmentName: string) => {
|
|
82
|
+
parseSegments(split).forEach((segmentName: string) => {
|
|
85
83
|
segments.add(segmentName);
|
|
86
84
|
});
|
|
87
85
|
} else {
|
|
88
|
-
accum.removed.push(
|
|
86
|
+
accum.removed.push(split);
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
return accum;
|
|
92
|
-
}, { added: [], removed: [] } as ISplitMutations
|
|
90
|
+
}, { added: [], removed: [], segments: [] } as ISplitMutations);
|
|
91
|
+
|
|
92
|
+
computed.segments = setToArray(segments);
|
|
93
|
+
|
|
94
|
+
return computed;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
/**
|
|
@@ -109,14 +111,14 @@ export function computeMutation<T extends ISplit | IRBSegment>(rules: Array<T>,
|
|
|
109
111
|
export function splitChangesUpdaterFactory(
|
|
110
112
|
log: ILogger,
|
|
111
113
|
splitChangesFetcher: ISplitChangesFetcher,
|
|
112
|
-
storage: Pick<IStorageBase, 'splits' | '
|
|
114
|
+
storage: Pick<IStorageBase, 'splits' | 'segments'>,
|
|
113
115
|
splitFiltersValidation: ISplitFiltersValidation,
|
|
114
116
|
splitsEventEmitter?: ISplitsEventEmitter,
|
|
115
117
|
requestTimeoutBeforeReady: number = 0,
|
|
116
118
|
retriesOnFailureBeforeReady: number = 0,
|
|
117
119
|
isClientSide?: boolean
|
|
118
|
-
):
|
|
119
|
-
const { splits,
|
|
120
|
+
): ISplitChangesUpdater {
|
|
121
|
+
const { splits, segments } = storage;
|
|
120
122
|
|
|
121
123
|
let startingUp = true;
|
|
122
124
|
|
|
@@ -133,53 +135,32 @@ export function splitChangesUpdaterFactory(
|
|
|
133
135
|
* @param noCache - true to revalidate data to fetch
|
|
134
136
|
* @param till - query param to bypass CDN requests
|
|
135
137
|
*/
|
|
136
|
-
return function splitChangesUpdater(noCache?: boolean, till?: number,
|
|
138
|
+
return function splitChangesUpdater(noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }) {
|
|
137
139
|
|
|
138
140
|
/**
|
|
139
141
|
* @param since - current changeNumber at splitsCache
|
|
140
142
|
* @param retry - current number of retry attempts
|
|
141
143
|
*/
|
|
142
|
-
function _splitChangesUpdater(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
instantUpdate.type === SPLIT_UPDATE ?
|
|
148
|
-
// IFFU edge case: a change to a flag that adds an IN_RULE_BASED_SEGMENT matcher that is not present yet
|
|
149
|
-
Promise.resolve(rbSegments.contains(parseSegments(instantUpdate.payload, IN_RULE_BASED_SEGMENT))).then((contains) => {
|
|
150
|
-
return contains ?
|
|
151
|
-
{ ff: { d: [instantUpdate.payload as ISplit], t: instantUpdate.changeNumber } } :
|
|
152
|
-
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator);
|
|
153
|
-
}) :
|
|
154
|
-
{ rbs: { d: [instantUpdate.payload as IRBSegment], t: instantUpdate.changeNumber } } :
|
|
155
|
-
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator)
|
|
144
|
+
function _splitChangesUpdater(since: number, retry = 0): Promise<boolean> {
|
|
145
|
+
log.debug(SYNC_SPLITS_FETCH, [since]);
|
|
146
|
+
return Promise.resolve(splitUpdateNotification ?
|
|
147
|
+
{ splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
|
|
148
|
+
splitChangesFetcher(since, noCache, till, _promiseDecorator)
|
|
156
149
|
)
|
|
157
150
|
.then((splitChanges: ISplitChangesResponse) => {
|
|
158
151
|
startingUp = false;
|
|
159
152
|
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
let ffUpdate: MaybeThenable<boolean> = false;
|
|
163
|
-
if (splitChanges.ff) {
|
|
164
|
-
const { added, removed } = computeMutation(splitChanges.ff.d, usedSegments, splitFiltersValidation);
|
|
165
|
-
log.debug(SYNC_SPLITS_UPDATE, [added.length, removed.length]);
|
|
166
|
-
ffUpdate = splits.update(added, removed, splitChanges.ff.t);
|
|
167
|
-
}
|
|
153
|
+
const mutation = computeSplitsMutation(splitChanges.splits, splitFiltersValidation);
|
|
168
154
|
|
|
169
|
-
|
|
170
|
-
if (splitChanges.rbs) {
|
|
171
|
-
const { added, removed } = computeMutation(splitChanges.rbs.d, usedSegments);
|
|
172
|
-
log.debug(SYNC_RBS_UPDATE, [added.length, removed.length]);
|
|
173
|
-
rbsUpdate = rbSegments.update(added, removed, splitChanges.rbs.t);
|
|
174
|
-
}
|
|
155
|
+
log.debug(SYNC_SPLITS_UPDATE, [mutation.added.length, mutation.removed.length, mutation.segments.length]);
|
|
175
156
|
|
|
176
|
-
return Promise.all([
|
|
177
|
-
|
|
178
|
-
segments.registerSegments(
|
|
179
|
-
]).then(([
|
|
157
|
+
return Promise.all([
|
|
158
|
+
splits.update(mutation.added, mutation.removed, splitChanges.till),
|
|
159
|
+
segments.registerSegments(mutation.segments)
|
|
160
|
+
]).then(([isThereUpdate]) => {
|
|
180
161
|
if (splitsEventEmitter) {
|
|
181
162
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
182
|
-
return Promise.resolve(!splitsEventEmitter.splitsArrived || (
|
|
163
|
+
return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && isThereUpdate && (isClientSide || checkAllSegmentsExist(segments))))
|
|
183
164
|
.catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
184
165
|
.then(emitSplitsArrivedEvent => {
|
|
185
166
|
// emit SDK events
|
|
@@ -196,24 +177,15 @@ export function splitChangesUpdaterFactory(
|
|
|
196
177
|
if (startingUp && retriesOnFailureBeforeReady > retry) {
|
|
197
178
|
retry += 1;
|
|
198
179
|
log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
|
|
199
|
-
return _splitChangesUpdater(
|
|
180
|
+
return _splitChangesUpdater(since, retry);
|
|
200
181
|
} else {
|
|
201
182
|
startingUp = false;
|
|
202
183
|
}
|
|
203
184
|
return false;
|
|
204
185
|
});
|
|
205
|
-
|
|
206
|
-
// After triggering the requests, if we have cached splits information let's notify that to emit SDK_READY_FROM_CACHE.
|
|
207
|
-
// Wrapping in a promise since checkCache can be async.
|
|
208
|
-
if (splitsEventEmitter && startingUp) {
|
|
209
|
-
Promise.resolve(splits.checkCache()).then(isCacheReady => {
|
|
210
|
-
if (isCacheReady) splitsEventEmitter.emit(SDK_SPLITS_CACHE_LOADED);
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
return fetcherPromise;
|
|
214
186
|
}
|
|
215
187
|
|
|
216
|
-
// `getChangeNumber` never rejects or throws error
|
|
217
|
-
return
|
|
188
|
+
let sincePromise = Promise.resolve(splits.getChangeNumber()); // `getChangeNumber` never rejects or throws error
|
|
189
|
+
return sincePromise.then(_splitChangesUpdater);
|
|
218
190
|
};
|
|
219
191
|
}
|