@splitsoftware/splitio-commons 2.3.0 → 2.3.1-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +4 -0
- package/README.md +1 -0
- package/cjs/evaluator/Engine.js +40 -61
- package/cjs/evaluator/combiners/and.js +2 -6
- package/cjs/evaluator/combiners/ifelseif.js +6 -6
- package/cjs/evaluator/condition/index.js +6 -5
- package/cjs/evaluator/index.js +11 -11
- package/cjs/evaluator/matchers/index.js +3 -1
- package/cjs/evaluator/matchers/matcherTypes.js +1 -0
- package/cjs/evaluator/matchers/prerequisites.js +22 -0
- package/cjs/evaluator/matchers/rbsegment.js +56 -0
- package/cjs/evaluator/matchersTransform/index.js +4 -0
- package/cjs/evaluator/parser/index.js +2 -2
- package/cjs/evaluator/value/sanitize.js +1 -0
- package/cjs/logger/constants.js +5 -3
- package/cjs/logger/messages/debug.js +4 -2
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/sdkManager/index.js +2 -1
- package/cjs/services/splitApi.js +3 -4
- package/cjs/services/splitHttpClient.js +1 -1
- package/cjs/storages/AbstractSplitsCacheSync.js +5 -2
- package/cjs/storages/KeyBuilder.js +9 -0
- package/cjs/storages/KeyBuilderCS.js +3 -0
- package/cjs/storages/KeyBuilderSS.js +3 -0
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +117 -0
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +9 -14
- package/cjs/storages/inLocalStorage/index.js +5 -1
- package/cjs/storages/inLocalStorage/validateCache.js +2 -1
- package/cjs/storages/inMemory/InMemoryStorage.js +3 -0
- package/cjs/storages/inMemory/InMemoryStorageCS.js +4 -0
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +61 -0
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +1 -0
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +64 -0
- package/cjs/storages/inRedis/index.js +2 -0
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +64 -0
- package/cjs/storages/pluggable/index.js +2 -0
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +54 -4
- package/cjs/sync/polling/pollingManagerCS.js +7 -7
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- 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 +59 -33
- package/cjs/sync/streaming/SSEHandler/index.js +1 -0
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +106 -77
- package/cjs/sync/streaming/constants.js +2 -1
- package/cjs/sync/streaming/pushManager.js +3 -16
- package/cjs/sync/syncManagerOnline.js +2 -2
- package/cjs/utils/constants/index.js +6 -2
- package/cjs/utils/labels/index.js +2 -1
- package/esm/evaluator/Engine.js +40 -62
- package/esm/evaluator/combiners/and.js +2 -6
- package/esm/evaluator/combiners/ifelseif.js +7 -7
- package/esm/evaluator/condition/index.js +6 -5
- package/esm/evaluator/index.js +12 -12
- package/esm/evaluator/matchers/index.js +3 -1
- package/esm/evaluator/matchers/matcherTypes.js +1 -0
- package/esm/evaluator/matchers/prerequisites.js +18 -0
- package/esm/evaluator/matchers/rbsegment.js +52 -0
- package/esm/evaluator/matchersTransform/index.js +4 -0
- package/esm/evaluator/parser/index.js +2 -2
- package/esm/evaluator/value/sanitize.js +1 -0
- package/esm/logger/constants.js +2 -0
- package/esm/logger/messages/debug.js +4 -2
- package/esm/logger/messages/warn.js +1 -1
- package/esm/sdkManager/index.js +2 -1
- package/esm/services/splitApi.js +3 -4
- package/esm/services/splitHttpClient.js +1 -1
- package/esm/storages/AbstractSplitsCacheSync.js +5 -2
- package/esm/storages/KeyBuilder.js +9 -0
- package/esm/storages/KeyBuilderCS.js +3 -0
- package/esm/storages/KeyBuilderSS.js +3 -0
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +114 -0
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +9 -14
- package/esm/storages/inLocalStorage/index.js +5 -1
- package/esm/storages/inLocalStorage/validateCache.js +2 -1
- package/esm/storages/inMemory/InMemoryStorage.js +3 -0
- package/esm/storages/inMemory/InMemoryStorageCS.js +4 -0
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +58 -0
- package/esm/storages/inMemory/SplitsCacheInMemory.js +1 -0
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +61 -0
- package/esm/storages/inRedis/index.js +2 -0
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +61 -0
- package/esm/storages/pluggable/index.js +2 -0
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +54 -4
- package/esm/sync/polling/pollingManagerCS.js +7 -7
- package/esm/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- 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 +59 -33
- package/esm/sync/streaming/SSEHandler/index.js +2 -1
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +102 -73
- package/esm/sync/streaming/constants.js +1 -0
- package/esm/sync/streaming/pushManager.js +6 -19
- package/esm/sync/syncManagerOnline.js +2 -2
- package/esm/utils/constants/index.js +5 -1
- package/esm/utils/labels/index.js +1 -0
- package/package.json +1 -1
- package/src/dtos/types.ts +41 -8
- package/src/evaluator/Engine.ts +42 -69
- package/src/evaluator/combiners/and.ts +5 -4
- package/src/evaluator/combiners/ifelseif.ts +7 -9
- package/src/evaluator/condition/engineUtils.ts +1 -1
- package/src/evaluator/condition/index.ts +12 -12
- package/src/evaluator/index.ts +12 -14
- package/src/evaluator/matchers/index.ts +3 -1
- package/src/evaluator/matchers/matcherTypes.ts +1 -0
- package/src/evaluator/matchers/prerequisites.ts +24 -0
- package/src/evaluator/matchers/rbsegment.ts +74 -0
- package/src/evaluator/matchersTransform/index.ts +3 -0
- 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 +5 -4
- package/src/logger/constants.ts +2 -0
- package/src/logger/messages/debug.ts +4 -2
- package/src/logger/messages/warn.ts +1 -1
- package/src/sdkManager/index.ts +3 -2
- package/src/services/splitApi.ts +3 -4
- package/src/services/splitHttpClient.ts +1 -1
- package/src/services/types.ts +1 -1
- package/src/storages/AbstractSplitsCacheSync.ts +6 -3
- package/src/storages/KeyBuilder.ts +12 -0
- package/src/storages/KeyBuilderCS.ts +4 -0
- package/src/storages/KeyBuilderSS.ts +4 -0
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +136 -0
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +10 -14
- package/src/storages/inLocalStorage/index.ts +5 -1
- package/src/storages/inLocalStorage/validateCache.ts +3 -1
- package/src/storages/inMemory/InMemoryStorage.ts +3 -0
- package/src/storages/inMemory/InMemoryStorageCS.ts +4 -0
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +68 -0
- package/src/storages/inMemory/SplitsCacheInMemory.ts +1 -0
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +79 -0
- package/src/storages/inRedis/index.ts +2 -0
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +76 -0
- package/src/storages/pluggable/index.ts +2 -0
- package/src/storages/types.ts +33 -1
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +65 -4
- package/src/sync/polling/fetchers/types.ts +1 -0
- package/src/sync/polling/pollingManagerCS.ts +7 -7
- package/src/sync/polling/syncTasks/splitsSyncTask.ts +1 -1
- 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 +70 -43
- package/src/sync/streaming/SSEHandler/index.ts +2 -1
- package/src/sync/streaming/SSEHandler/types.ts +2 -2
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +98 -68
- package/src/sync/streaming/constants.ts +1 -0
- package/src/sync/streaming/parseUtils.ts +2 -2
- package/src/sync/streaming/pushManager.ts +6 -18
- package/src/sync/streaming/types.ts +3 -2
- package/src/sync/syncManagerOnline.ts +2 -2
- package/src/utils/constants/index.ts +6 -1
- package/src/utils/labels/index.ts +1 -0
- package/src/utils/lang/index.ts +1 -1
- package/types/splitio.d.ts +5 -1
|
@@ -1,24 +1,85 @@
|
|
|
1
|
+
import { ISettings } from '../../../types';
|
|
2
|
+
import { ISplitChangesResponse } from '../../../dtos/types';
|
|
1
3
|
import { IFetchSplitChanges, IResponse } from '../../../services/types';
|
|
4
|
+
import { IStorageBase } from '../../../storages/types';
|
|
5
|
+
import { FLAG_SPEC_VERSION } from '../../../utils/constants';
|
|
6
|
+
import { base } from '../../../utils/settingsValidation';
|
|
2
7
|
import { ISplitChangesFetcher } from './types';
|
|
8
|
+
import { LOG_PREFIX_SYNC_SPLITS } from '../../../logger/constants';
|
|
9
|
+
|
|
10
|
+
const PROXY_CHECK_INTERVAL_MILLIS_CS = 60 * 60 * 1000; // 1 hour in Client Side
|
|
11
|
+
const PROXY_CHECK_INTERVAL_MILLIS_SS = 24 * PROXY_CHECK_INTERVAL_MILLIS_CS; // 24 hours in Server Side
|
|
12
|
+
|
|
13
|
+
function sdkEndpointOverridden(settings: ISettings) {
|
|
14
|
+
return settings.urls.sdk !== base.urls.sdk;
|
|
15
|
+
}
|
|
3
16
|
|
|
4
17
|
/**
|
|
5
18
|
* Factory of SplitChanges fetcher.
|
|
6
19
|
* SplitChanges fetcher is a wrapper around `splitChanges` API service that parses the response and handle errors.
|
|
7
20
|
*/
|
|
8
|
-
|
|
21
|
+
// @TODO breaking: drop support for Split Proxy below v5.10.0 and simplify the implementation
|
|
22
|
+
export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges, settings: ISettings, storage: Pick<IStorageBase, 'splits' | 'rbSegments'>): ISplitChangesFetcher {
|
|
23
|
+
|
|
24
|
+
const log = settings.log;
|
|
25
|
+
const PROXY_CHECK_INTERVAL_MILLIS = settings.core.key !== undefined ? PROXY_CHECK_INTERVAL_MILLIS_CS : PROXY_CHECK_INTERVAL_MILLIS_SS;
|
|
26
|
+
let lastProxyCheckTimestamp: number | undefined;
|
|
9
27
|
|
|
10
28
|
return function splitChangesFetcher(
|
|
11
29
|
since: number,
|
|
12
30
|
noCache?: boolean,
|
|
13
31
|
till?: number,
|
|
32
|
+
rbSince?: number,
|
|
14
33
|
// Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
|
|
15
34
|
decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
|
|
16
|
-
) {
|
|
35
|
+
): Promise<ISplitChangesResponse> {
|
|
36
|
+
|
|
37
|
+
// Recheck proxy
|
|
38
|
+
if (lastProxyCheckTimestamp && (Date.now() - lastProxyCheckTimestamp) > PROXY_CHECK_INTERVAL_MILLIS) {
|
|
39
|
+
settings.sync.flagSpecVersion = FLAG_SPEC_VERSION;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let splitsPromise = fetchSplitChanges(since, noCache, till, settings.sync.flagSpecVersion === FLAG_SPEC_VERSION ? rbSince : undefined)
|
|
43
|
+
// Handle proxy error with spec 1.3
|
|
44
|
+
.catch((err) => {
|
|
45
|
+
if (err.statusCode === 400 && sdkEndpointOverridden(settings) && settings.sync.flagSpecVersion === FLAG_SPEC_VERSION) {
|
|
46
|
+
log.error(LOG_PREFIX_SYNC_SPLITS + 'Proxy error detected. Retrying with spec 1.2. If you are using Split Proxy, please upgrade to latest version');
|
|
47
|
+
lastProxyCheckTimestamp = Date.now();
|
|
48
|
+
settings.sync.flagSpecVersion = '1.2'; // fallback to 1.2 spec
|
|
49
|
+
return fetchSplitChanges(since, noCache, till); // retry request without rbSince
|
|
50
|
+
}
|
|
51
|
+
throw err;
|
|
52
|
+
});
|
|
17
53
|
|
|
18
|
-
let splitsPromise = fetchSplitChanges(since, noCache, till);
|
|
19
54
|
if (decorator) splitsPromise = decorator(splitsPromise);
|
|
20
55
|
|
|
21
|
-
return splitsPromise
|
|
56
|
+
return splitsPromise
|
|
57
|
+
.then(resp => resp.json())
|
|
58
|
+
.then(data => {
|
|
59
|
+
// Using flag spec version 1.2 or below
|
|
60
|
+
if (data.splits) {
|
|
61
|
+
return {
|
|
62
|
+
ff: {
|
|
63
|
+
d: data.splits,
|
|
64
|
+
s: data.since,
|
|
65
|
+
t: data.till
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Proxy recovery
|
|
71
|
+
if (lastProxyCheckTimestamp) {
|
|
72
|
+
log.info(LOG_PREFIX_SYNC_SPLITS + 'Proxy error recovered');
|
|
73
|
+
lastProxyCheckTimestamp = undefined;
|
|
74
|
+
return splitChangesFetcher(-1, undefined, undefined, -1)
|
|
75
|
+
.then((splitChangesResponse: ISplitChangesResponse) =>
|
|
76
|
+
Promise.all([storage.splits.clear(), storage.rbSegments.clear()])
|
|
77
|
+
.then(() => splitChangesResponse)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return data;
|
|
82
|
+
});
|
|
22
83
|
};
|
|
23
84
|
|
|
24
85
|
}
|
|
@@ -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 usingSegments = storage.splits.usesSegments() || storage.rbSegments.usesSegments();
|
|
47
|
+
if (usingSegments !== mySegmentsSyncTask.isRunning()) {
|
|
48
|
+
log.info(POLLING_SMART_PAUSING, [usingSegments ? 'ON' : 'OFF']);
|
|
49
|
+
if (usingSegments) {
|
|
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()) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
62
|
+
if (!readiness.isReady() && !storage.splits.usesSegments() && !storage.rbSegments.usesSegments()) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
63
63
|
}
|
|
64
|
-
if (!storage.splits.usesSegments()) setTimeout(smartReady, 0);
|
|
64
|
+
if (!storage.splits.usesSegments() && !storage.rbSegments.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()) startMySegmentsSyncTasks();
|
|
80
|
+
if (storage.splits.usesSegments() || storage.rbSegments.usesSegments()) startMySegmentsSyncTasks();
|
|
81
81
|
},
|
|
82
82
|
|
|
83
83
|
// Stop periodic fetching (polling)
|
|
@@ -21,7 +21,7 @@ export function splitsSyncTaskFactory(
|
|
|
21
21
|
settings.log,
|
|
22
22
|
splitChangesUpdaterFactory(
|
|
23
23
|
settings.log,
|
|
24
|
-
splitChangesFetcherFactory(fetchSplitChanges),
|
|
24
|
+
splitChangesFetcherFactory(fetchSplitChanges, settings, storage),
|
|
25
25
|
storage,
|
|
26
26
|
settings.sync.__splitFiltersValidation,
|
|
27
27
|
readiness.splits,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ISplit } from '../../dtos/types';
|
|
1
|
+
import { IRBSegment, 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, changeNumber: number }], boolean> { }
|
|
7
|
+
export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit | IRBSegment, 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, segments, largeSegments } = storage;
|
|
30
|
+
const { splits, rbSegments, 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 (splits.usesSegments() && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
54
|
+
if ((splits.usesSegments() || rbSegments.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 notifications.
|
|
54
|
+
* This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE or RB_SEGMENT_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,16 +1,18 @@
|
|
|
1
1
|
import { ISegmentsCacheBase, IStorageBase } from '../../../storages/types';
|
|
2
2
|
import { ISplitChangesFetcher } from '../fetchers/types';
|
|
3
|
-
import { ISplit, ISplitChangesResponse, ISplitFiltersValidation } from '../../../dtos/types';
|
|
3
|
+
import { IRBSegment, ISplit, ISplitChangesResponse, ISplitFiltersValidation, MaybeThenable } from '../../../dtos/types';
|
|
4
4
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
5
5
|
import { timeout } from '../../../utils/promise/timeout';
|
|
6
6
|
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
7
7
|
import { ILogger } from '../../../logger/types';
|
|
8
|
-
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
8
|
+
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_RBS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
9
9
|
import { startsWith } from '../../../utils/lang';
|
|
10
|
-
import { IN_SEGMENT } from '../../../utils/constants';
|
|
10
|
+
import { IN_RULE_BASED_SEGMENT, IN_SEGMENT, RULE_BASED_SEGMENT, STANDARD_SEGMENT } from '../../../utils/constants';
|
|
11
11
|
import { setToArray } from '../../../utils/lang/sets';
|
|
12
|
+
import { SPLIT_UPDATE } from '../../streaming/constants';
|
|
12
13
|
|
|
13
|
-
type
|
|
14
|
+
export type InstantUpdate = { payload: ISplit | IRBSegment, changeNumber: number, type: string };
|
|
15
|
+
type SplitChangesUpdater = (noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) => Promise<boolean>
|
|
14
16
|
|
|
15
17
|
// Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
|
|
16
18
|
// Returns a promise that could be rejected.
|
|
@@ -24,27 +26,35 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise<boolean> {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
|
-
* Collect segments from a raw
|
|
29
|
+
* Collect segments from a raw FF or RBS definition.
|
|
28
30
|
* Exported for testing purposes.
|
|
29
31
|
*/
|
|
30
|
-
export function parseSegments(
|
|
31
|
-
|
|
32
|
+
export function parseSegments(ruleEntity: ISplit | IRBSegment, matcherType: typeof IN_SEGMENT | typeof IN_RULE_BASED_SEGMENT = IN_SEGMENT): Set<string> {
|
|
33
|
+
const { conditions = [], excluded } = ruleEntity as IRBSegment;
|
|
34
|
+
|
|
35
|
+
const segments = new Set<string>();
|
|
36
|
+
if (excluded && excluded.segments) {
|
|
37
|
+
excluded.segments.forEach(({ type, name }) => {
|
|
38
|
+
if ((type === STANDARD_SEGMENT && matcherType === IN_SEGMENT) || (type === RULE_BASED_SEGMENT && matcherType === IN_RULE_BASED_SEGMENT)) {
|
|
39
|
+
segments.add(name);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
32
43
|
|
|
33
44
|
for (let i = 0; i < conditions.length; i++) {
|
|
34
45
|
const matchers = conditions[i].matcherGroup.matchers;
|
|
35
46
|
|
|
36
47
|
matchers.forEach(matcher => {
|
|
37
|
-
if (matcher.matcherType ===
|
|
48
|
+
if (matcher.matcherType === matcherType) segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
|
|
38
49
|
});
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
return segments;
|
|
42
53
|
}
|
|
43
54
|
|
|
44
|
-
interface ISplitMutations {
|
|
45
|
-
added:
|
|
46
|
-
removed:
|
|
47
|
-
segments: string[]
|
|
55
|
+
interface ISplitMutations<T extends ISplit | IRBSegment> {
|
|
56
|
+
added: T[],
|
|
57
|
+
removed: T[]
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
/**
|
|
@@ -73,25 +83,21 @@ function matchFilters(featureFlag: ISplit, filters: ISplitFiltersValidation) {
|
|
|
73
83
|
* i.e., an object with added splits, removed splits and used segments.
|
|
74
84
|
* Exported for testing purposes.
|
|
75
85
|
*/
|
|
76
|
-
export function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
accum.added.push(
|
|
86
|
+
export function computeMutation<T extends ISplit | IRBSegment>(rules: Array<T>, segments: Set<string>, filters?: ISplitFiltersValidation): ISplitMutations<T> {
|
|
87
|
+
|
|
88
|
+
return rules.reduce((accum, ruleEntity) => {
|
|
89
|
+
if (ruleEntity.status === 'ACTIVE' && (!filters || matchFilters(ruleEntity as ISplit, filters))) {
|
|
90
|
+
accum.added.push(ruleEntity);
|
|
81
91
|
|
|
82
|
-
parseSegments(
|
|
92
|
+
parseSegments(ruleEntity).forEach((segmentName: string) => {
|
|
83
93
|
segments.add(segmentName);
|
|
84
94
|
});
|
|
85
95
|
} else {
|
|
86
|
-
accum.removed.push(
|
|
96
|
+
accum.removed.push(ruleEntity);
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
return accum;
|
|
90
|
-
}, { added: [], removed: []
|
|
91
|
-
|
|
92
|
-
computed.segments = setToArray(segments);
|
|
93
|
-
|
|
94
|
-
return computed;
|
|
100
|
+
}, { added: [], removed: [] } as ISplitMutations<T>);
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
/**
|
|
@@ -111,14 +117,14 @@ export function computeSplitsMutation(entries: ISplit[], filters: ISplitFiltersV
|
|
|
111
117
|
export function splitChangesUpdaterFactory(
|
|
112
118
|
log: ILogger,
|
|
113
119
|
splitChangesFetcher: ISplitChangesFetcher,
|
|
114
|
-
storage: Pick<IStorageBase, 'splits' | 'segments'>,
|
|
120
|
+
storage: Pick<IStorageBase, 'splits' | 'rbSegments' | 'segments'>,
|
|
115
121
|
splitFiltersValidation: ISplitFiltersValidation,
|
|
116
122
|
splitsEventEmitter?: ISplitsEventEmitter,
|
|
117
123
|
requestTimeoutBeforeReady: number = 0,
|
|
118
124
|
retriesOnFailureBeforeReady: number = 0,
|
|
119
125
|
isClientSide?: boolean
|
|
120
|
-
):
|
|
121
|
-
const { splits, segments } = storage;
|
|
126
|
+
): SplitChangesUpdater {
|
|
127
|
+
const { splits, rbSegments, segments } = storage;
|
|
122
128
|
|
|
123
129
|
let startingUp = true;
|
|
124
130
|
|
|
@@ -135,32 +141,53 @@ export function splitChangesUpdaterFactory(
|
|
|
135
141
|
* @param noCache - true to revalidate data to fetch
|
|
136
142
|
* @param till - query param to bypass CDN requests
|
|
137
143
|
*/
|
|
138
|
-
return function splitChangesUpdater(noCache?: boolean, till?: number,
|
|
144
|
+
return function splitChangesUpdater(noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) {
|
|
139
145
|
|
|
140
146
|
/**
|
|
141
147
|
* @param since - current changeNumber at splitsCache
|
|
142
148
|
* @param retry - current number of retry attempts
|
|
143
149
|
*/
|
|
144
|
-
function _splitChangesUpdater(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
150
|
+
function _splitChangesUpdater(sinces: [number, number], retry = 0): Promise<boolean> {
|
|
151
|
+
const [since, rbSince] = sinces;
|
|
152
|
+
log.debug(SYNC_SPLITS_FETCH, sinces);
|
|
153
|
+
return Promise.resolve(
|
|
154
|
+
instantUpdate ?
|
|
155
|
+
instantUpdate.type === SPLIT_UPDATE ?
|
|
156
|
+
// IFFU edge case: a change to a flag that adds an IN_RULE_BASED_SEGMENT matcher that is not present yet
|
|
157
|
+
Promise.resolve(rbSegments.contains(parseSegments(instantUpdate.payload, IN_RULE_BASED_SEGMENT))).then((contains) => {
|
|
158
|
+
return contains ?
|
|
159
|
+
{ ff: { d: [instantUpdate.payload as ISplit], t: instantUpdate.changeNumber } } :
|
|
160
|
+
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator);
|
|
161
|
+
}) :
|
|
162
|
+
{ rbs: { d: [instantUpdate.payload as IRBSegment], t: instantUpdate.changeNumber } } :
|
|
163
|
+
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator)
|
|
149
164
|
)
|
|
150
165
|
.then((splitChanges: ISplitChangesResponse) => {
|
|
151
166
|
startingUp = false;
|
|
152
167
|
|
|
153
|
-
const
|
|
168
|
+
const usedSegments = new Set<string>();
|
|
154
169
|
|
|
155
|
-
|
|
170
|
+
let ffUpdate: MaybeThenable<boolean> = false;
|
|
171
|
+
if (splitChanges.ff) {
|
|
172
|
+
const { added, removed } = computeMutation(splitChanges.ff.d, usedSegments, splitFiltersValidation);
|
|
173
|
+
log.debug(SYNC_SPLITS_UPDATE, [added.length, removed.length]);
|
|
174
|
+
ffUpdate = splits.update(added, removed, splitChanges.ff.t);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let rbsUpdate: MaybeThenable<boolean> = false;
|
|
178
|
+
if (splitChanges.rbs) {
|
|
179
|
+
const { added, removed } = computeMutation(splitChanges.rbs.d, usedSegments);
|
|
180
|
+
log.debug(SYNC_RBS_UPDATE, [added.length, removed.length]);
|
|
181
|
+
rbsUpdate = rbSegments.update(added, removed, splitChanges.rbs.t);
|
|
182
|
+
}
|
|
156
183
|
|
|
157
|
-
return Promise.all([
|
|
158
|
-
|
|
159
|
-
segments.registerSegments(
|
|
160
|
-
]).then(([
|
|
184
|
+
return Promise.all([ffUpdate, rbsUpdate,
|
|
185
|
+
// @TODO if at least 1 segment fetch fails due to 404 and other segments are updated in the storage, SDK_UPDATE is not emitted
|
|
186
|
+
segments.registerSegments(setToArray(usedSegments))
|
|
187
|
+
]).then(([ffChanged, rbsChanged]) => {
|
|
161
188
|
if (splitsEventEmitter) {
|
|
162
189
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
163
|
-
return Promise.resolve(!splitsEventEmitter.splitsArrived || (
|
|
190
|
+
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
164
191
|
.catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
165
192
|
.then(emitSplitsArrivedEvent => {
|
|
166
193
|
// emit SDK events
|
|
@@ -177,7 +204,7 @@ export function splitChangesUpdaterFactory(
|
|
|
177
204
|
if (startingUp && retriesOnFailureBeforeReady > retry) {
|
|
178
205
|
retry += 1;
|
|
179
206
|
log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
|
|
180
|
-
return _splitChangesUpdater(
|
|
207
|
+
return _splitChangesUpdater(sinces, retry);
|
|
181
208
|
} else {
|
|
182
209
|
startingUp = false;
|
|
183
210
|
}
|
|
@@ -185,7 +212,7 @@ export function splitChangesUpdaterFactory(
|
|
|
185
212
|
});
|
|
186
213
|
}
|
|
187
214
|
|
|
188
|
-
|
|
189
|
-
return
|
|
215
|
+
// `getChangeNumber` never rejects or throws error
|
|
216
|
+
return Promise.all([splits.getChangeNumber(), rbSegments.getChangeNumber()]).then(_splitChangesUpdater);
|
|
190
217
|
};
|
|
191
218
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { errorParser, messageParser } from './NotificationParser';
|
|
2
2
|
import { notificationKeeperFactory } from './NotificationKeeper';
|
|
3
|
-
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE } from '../constants';
|
|
3
|
+
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, RB_SEGMENT_UPDATE } from '../constants';
|
|
4
4
|
import { IPushEventEmitter } from '../types';
|
|
5
5
|
import { ISseEventHandler } from '../SSEClient/types';
|
|
6
6
|
import { INotificationError, INotificationMessage } from './types';
|
|
@@ -84,6 +84,7 @@ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter,
|
|
|
84
84
|
case MEMBERSHIPS_MS_UPDATE:
|
|
85
85
|
case MEMBERSHIPS_LS_UPDATE:
|
|
86
86
|
case SPLIT_KILL:
|
|
87
|
+
case RB_SEGMENT_UPDATE:
|
|
87
88
|
pushEmitter.emit(parsedData.type, parsedData);
|
|
88
89
|
break;
|
|
89
90
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ControlType } from '../constants';
|
|
2
|
-
import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../types';
|
|
2
|
+
import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE, RB_SEGMENT_UPDATE } from '../types';
|
|
3
3
|
|
|
4
4
|
export enum Compression {
|
|
5
5
|
None = 0,
|
|
@@ -42,7 +42,7 @@ export interface ISegmentUpdateData {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export interface ISplitUpdateData {
|
|
45
|
-
type: SPLIT_UPDATE,
|
|
45
|
+
type: SPLIT_UPDATE | RB_SEGMENT_UPDATE,
|
|
46
46
|
changeNumber: number,
|
|
47
47
|
pcn?: number,
|
|
48
48
|
d?: string,
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import { ISplit } from '../../../dtos/types';
|
|
1
|
+
import { IRBSegment, ISplit } from '../../../dtos/types';
|
|
2
|
+
import { STREAMING_PARSING_SPLIT_UPDATE } from '../../../logger/constants';
|
|
2
3
|
import { ILogger } from '../../../logger/types';
|
|
3
4
|
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
4
5
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
5
|
-
import { ISplitsCacheSync } from '../../../storages/types';
|
|
6
|
+
import { IRBSegmentsCacheSync, ISplitsCacheSync, IStorageSync } from '../../../storages/types';
|
|
6
7
|
import { ITelemetryTracker } from '../../../trackers/types';
|
|
7
8
|
import { Backoff } from '../../../utils/Backoff';
|
|
8
9
|
import { SPLITS } from '../../../utils/constants';
|
|
9
10
|
import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
|
|
11
|
+
import { InstantUpdate } from '../../polling/updaters/splitChangesUpdater';
|
|
12
|
+
import { RB_SEGMENT_UPDATE } from '../constants';
|
|
13
|
+
import { parseFFUpdatePayload } from '../parseUtils';
|
|
10
14
|
import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
|
|
11
15
|
import { FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT, FETCH_BACKOFF_MAX_RETRIES } from './constants';
|
|
12
16
|
import { IUpdateWorker } from './types';
|
|
@@ -14,102 +18,128 @@ import { IUpdateWorker } from './types';
|
|
|
14
18
|
/**
|
|
15
19
|
* SplitsUpdateWorker factory
|
|
16
20
|
*/
|
|
17
|
-
export function SplitsUpdateWorker(log: ILogger,
|
|
21
|
+
export function SplitsUpdateWorker(log: ILogger, storage: IStorageSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, telemetryTracker: ITelemetryTracker, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker<[updateData: ISplitUpdateData]> & { killSplit(event: ISplitKillData): void } {
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
let isHandlingEvent: boolean;
|
|
22
|
-
let cdnBypass: boolean;
|
|
23
|
-
let payload: ISplit | undefined;
|
|
24
|
-
const backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
|
|
23
|
+
const ff = SplitsUpdateWorker(storage.splits);
|
|
24
|
+
const rbs = SplitsUpdateWorker(storage.rbSegments);
|
|
25
25
|
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (!isHandlingEvent) return; // halt if `stop` has been called
|
|
34
|
-
if (handleNewEvent) {
|
|
35
|
-
__handleSplitUpdateCall();
|
|
36
|
-
} else {
|
|
37
|
-
if (splitUpdateNotification) telemetryTracker.trackUpdatesFromSSE(SPLITS);
|
|
38
|
-
// fetch new registered segments for server-side API. Not retrying on error
|
|
39
|
-
if (segmentsSyncTask) segmentsSyncTask.execute(true);
|
|
26
|
+
function SplitsUpdateWorker(cache: ISplitsCacheSync | IRBSegmentsCacheSync) {
|
|
27
|
+
let maxChangeNumber = -1;
|
|
28
|
+
let handleNewEvent = false;
|
|
29
|
+
let isHandlingEvent: boolean;
|
|
30
|
+
let cdnBypass: boolean;
|
|
31
|
+
let instantUpdate: InstantUpdate | undefined;
|
|
32
|
+
const backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
|
|
40
33
|
|
|
41
|
-
|
|
34
|
+
function __handleSplitUpdateCall() {
|
|
35
|
+
isHandlingEvent = true;
|
|
36
|
+
if (maxChangeNumber > cache.getChangeNumber()) {
|
|
37
|
+
handleNewEvent = false;
|
|
38
|
+
// fetch splits revalidating data if cached
|
|
39
|
+
splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined, instantUpdate).then(() => {
|
|
40
|
+
if (!isHandlingEvent) return; // halt if `stop` has been called
|
|
41
|
+
if (handleNewEvent) {
|
|
42
|
+
__handleSplitUpdateCall();
|
|
43
|
+
} else {
|
|
44
|
+
if (instantUpdate) telemetryTracker.trackUpdatesFromSSE(SPLITS);
|
|
45
|
+
// fetch new registered segments for server-side API. Not retrying on error
|
|
46
|
+
if (segmentsSyncTask) segmentsSyncTask.execute(true);
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
|
|
45
|
-
isHandlingEvent = false;
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
+
const attempts = backoff.attempts + 1;
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
if (ff.isSync() && rbs.isSync()) {
|
|
51
|
+
log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
|
|
52
|
+
isHandlingEvent = false;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
cdnBypass
|
|
60
|
-
|
|
56
|
+
if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
|
|
57
|
+
backoff.scheduleCall();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (cdnBypass) {
|
|
62
|
+
log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
|
|
63
|
+
isHandlingEvent = false;
|
|
64
|
+
} else {
|
|
65
|
+
backoff.reset();
|
|
66
|
+
cdnBypass = true;
|
|
67
|
+
__handleSplitUpdateCall();
|
|
68
|
+
}
|
|
61
69
|
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
isHandlingEvent = false;
|
|
73
|
+
}
|
|
66
74
|
}
|
|
67
|
-
}
|
|
68
75
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
return {
|
|
77
|
+
/**
|
|
78
|
+
* Invoked by NotificationProcessor on SPLIT_UPDATE or RB_SEGMENT_UPDATE event
|
|
79
|
+
*
|
|
80
|
+
* @param changeNumber - change number of the notification
|
|
81
|
+
*/
|
|
82
|
+
put({ changeNumber, pcn, type }: ISplitUpdateData, payload?: ISplit | IRBSegment) {
|
|
83
|
+
const currentChangeNumber = cache.getChangeNumber();
|
|
76
84
|
|
|
77
|
-
|
|
85
|
+
if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
maxChangeNumber = changeNumber;
|
|
88
|
+
handleNewEvent = true;
|
|
89
|
+
cdnBypass = false;
|
|
90
|
+
instantUpdate = undefined;
|
|
83
91
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
if (payload && currentChangeNumber === pcn) {
|
|
93
|
+
instantUpdate = { payload, changeNumber, type };
|
|
94
|
+
}
|
|
87
95
|
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
if (backoff.timeoutID || !isHandlingEvent) __handleSplitUpdateCall();
|
|
97
|
+
backoff.reset();
|
|
98
|
+
},
|
|
99
|
+
stop() {
|
|
100
|
+
isHandlingEvent = false;
|
|
101
|
+
backoff.reset();
|
|
102
|
+
},
|
|
103
|
+
isSync() {
|
|
104
|
+
return maxChangeNumber <= cache.getChangeNumber();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
90
107
|
}
|
|
91
108
|
|
|
92
109
|
return {
|
|
93
|
-
put
|
|
110
|
+
put(parsedData) {
|
|
111
|
+
if (parsedData.d && parsedData.c !== undefined) {
|
|
112
|
+
try {
|
|
113
|
+
const payload = parseFFUpdatePayload(parsedData.c, parsedData.d);
|
|
114
|
+
if (payload) {
|
|
115
|
+
(parsedData.type === RB_SEGMENT_UPDATE ? rbs : ff).put(parsedData, payload);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
} catch (e) {
|
|
119
|
+
log.warn(STREAMING_PARSING_SPLIT_UPDATE, [parsedData.type, e]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
(parsedData.type === RB_SEGMENT_UPDATE ? rbs : ff).put(parsedData);
|
|
123
|
+
},
|
|
94
124
|
/**
|
|
95
125
|
* Invoked by NotificationProcessor on SPLIT_KILL event
|
|
96
126
|
*
|
|
97
|
-
* @param changeNumber - change number of the
|
|
127
|
+
* @param changeNumber - change number of the notification
|
|
98
128
|
* @param splitName - name of split to kill
|
|
99
129
|
* @param defaultTreatment - default treatment value
|
|
100
130
|
*/
|
|
101
131
|
killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
|
|
102
|
-
if (
|
|
132
|
+
if (storage.splits.killLocally(splitName, defaultTreatment, changeNumber)) {
|
|
103
133
|
// trigger an SDK_UPDATE if Split was killed locally
|
|
104
134
|
splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
|
|
105
135
|
}
|
|
106
136
|
// queues the SplitChanges fetch (only if changeNumber is newer)
|
|
107
|
-
put({ changeNumber } as ISplitUpdateData);
|
|
137
|
+
ff.put({ changeNumber } as ISplitUpdateData);
|
|
108
138
|
},
|
|
109
139
|
|
|
110
140
|
stop() {
|
|
111
|
-
|
|
112
|
-
|
|
141
|
+
ff.stop();
|
|
142
|
+
rbs.stop();
|
|
113
143
|
}
|
|
114
144
|
};
|
|
115
145
|
}
|
|
@@ -30,6 +30,7 @@ export const MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
|
|
|
30
30
|
export const SEGMENT_UPDATE = 'SEGMENT_UPDATE';
|
|
31
31
|
export const SPLIT_KILL = 'SPLIT_KILL';
|
|
32
32
|
export const SPLIT_UPDATE = 'SPLIT_UPDATE';
|
|
33
|
+
export const RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE';
|
|
33
34
|
|
|
34
35
|
// Control-type push notifications, handled by NotificationKeeper
|
|
35
36
|
export const CONTROL = 'CONTROL';
|