@splitsoftware/splitio-commons 2.2.0 → 2.2.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 +44 -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/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/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 +51 -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 +51 -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 +3 -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 +40 -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/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/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 +51 -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 +51 -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 +2 -1
- package/esm/utils/labels/index.js +1 -0
- package/package.json +1 -1
- package/src/dtos/types.ts +36 -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 +62 -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/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/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 +62 -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 +61 -42
- 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 +2 -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,82 @@
|
|
|
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 sdkEndpointOverriden(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 && sdkEndpointOverriden(settings) && settings.sync.flagSpecVersion === FLAG_SPEC_VERSION) {
|
|
46
|
+
log.error(LOG_PREFIX_SYNC_SPLITS + 'Proxy error detected. 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
|
|
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 Promise.all([storage.splits.clear(), storage.rbSegments.clear()])
|
|
75
|
+
.then(() => splitChangesFetcher(storage.splits.getChangeNumber() as number, undefined, undefined, storage.rbSegments.getChangeNumber() as number));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return data;
|
|
79
|
+
});
|
|
22
80
|
};
|
|
23
81
|
|
|
24
82
|
}
|
|
@@ -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 } 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.
|
|
@@ -27,24 +29,24 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise<boolean> {
|
|
|
27
29
|
* Collect segments from a raw split 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
|
+
const segments = new Set<string>(excluded && excluded.segments);
|
|
32
35
|
|
|
33
36
|
for (let i = 0; i < conditions.length; i++) {
|
|
34
37
|
const matchers = conditions[i].matcherGroup.matchers;
|
|
35
38
|
|
|
36
39
|
matchers.forEach(matcher => {
|
|
37
|
-
if (matcher.matcherType ===
|
|
40
|
+
if (matcher.matcherType === matcherType) segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
|
|
38
41
|
});
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
return segments;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
interface ISplitMutations {
|
|
45
|
-
added:
|
|
46
|
-
removed:
|
|
47
|
-
segments: string[]
|
|
47
|
+
interface ISplitMutations<T extends ISplit | IRBSegment> {
|
|
48
|
+
added: T[],
|
|
49
|
+
removed: T[]
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
/**
|
|
@@ -73,25 +75,21 @@ function matchFilters(featureFlag: ISplit, filters: ISplitFiltersValidation) {
|
|
|
73
75
|
* i.e., an object with added splits, removed splits and used segments.
|
|
74
76
|
* Exported for testing purposes.
|
|
75
77
|
*/
|
|
76
|
-
export function
|
|
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);
|
|
78
|
+
export function computeMutation<T extends ISplit | IRBSegment>(rules: Array<T>, segments: Set<string>, filters?: ISplitFiltersValidation): ISplitMutations<T> {
|
|
81
79
|
|
|
82
|
-
|
|
80
|
+
return rules.reduce((accum, ruleEntity) => {
|
|
81
|
+
if (ruleEntity.status === 'ACTIVE' && (!filters || matchFilters(ruleEntity as ISplit, filters))) {
|
|
82
|
+
accum.added.push(ruleEntity);
|
|
83
|
+
|
|
84
|
+
parseSegments(ruleEntity).forEach((segmentName: string) => {
|
|
83
85
|
segments.add(segmentName);
|
|
84
86
|
});
|
|
85
87
|
} else {
|
|
86
|
-
accum.removed.push(
|
|
88
|
+
accum.removed.push(ruleEntity);
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
return accum;
|
|
90
|
-
}, { added: [], removed: []
|
|
91
|
-
|
|
92
|
-
computed.segments = setToArray(segments);
|
|
93
|
-
|
|
94
|
-
return computed;
|
|
92
|
+
}, { added: [], removed: [] } as ISplitMutations<T>);
|
|
95
93
|
}
|
|
96
94
|
|
|
97
95
|
/**
|
|
@@ -111,14 +109,14 @@ export function computeSplitsMutation(entries: ISplit[], filters: ISplitFiltersV
|
|
|
111
109
|
export function splitChangesUpdaterFactory(
|
|
112
110
|
log: ILogger,
|
|
113
111
|
splitChangesFetcher: ISplitChangesFetcher,
|
|
114
|
-
storage: Pick<IStorageBase, 'splits' | 'segments'>,
|
|
112
|
+
storage: Pick<IStorageBase, 'splits' | 'rbSegments' | 'segments'>,
|
|
115
113
|
splitFiltersValidation: ISplitFiltersValidation,
|
|
116
114
|
splitsEventEmitter?: ISplitsEventEmitter,
|
|
117
115
|
requestTimeoutBeforeReady: number = 0,
|
|
118
116
|
retriesOnFailureBeforeReady: number = 0,
|
|
119
117
|
isClientSide?: boolean
|
|
120
|
-
):
|
|
121
|
-
const { splits, segments } = storage;
|
|
118
|
+
): SplitChangesUpdater {
|
|
119
|
+
const { splits, rbSegments, segments } = storage;
|
|
122
120
|
|
|
123
121
|
let startingUp = true;
|
|
124
122
|
|
|
@@ -135,32 +133,53 @@ export function splitChangesUpdaterFactory(
|
|
|
135
133
|
* @param noCache - true to revalidate data to fetch
|
|
136
134
|
* @param till - query param to bypass CDN requests
|
|
137
135
|
*/
|
|
138
|
-
return function splitChangesUpdater(noCache?: boolean, till?: number,
|
|
136
|
+
return function splitChangesUpdater(noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) {
|
|
139
137
|
|
|
140
138
|
/**
|
|
141
139
|
* @param since - current changeNumber at splitsCache
|
|
142
140
|
* @param retry - current number of retry attempts
|
|
143
141
|
*/
|
|
144
|
-
function _splitChangesUpdater(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
142
|
+
function _splitChangesUpdater(sinces: [number, number], retry = 0): Promise<boolean> {
|
|
143
|
+
const [since, rbSince] = sinces;
|
|
144
|
+
log.debug(SYNC_SPLITS_FETCH, sinces);
|
|
145
|
+
return Promise.resolve(
|
|
146
|
+
instantUpdate ?
|
|
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)
|
|
149
156
|
)
|
|
150
157
|
.then((splitChanges: ISplitChangesResponse) => {
|
|
151
158
|
startingUp = false;
|
|
152
159
|
|
|
153
|
-
const
|
|
160
|
+
const usedSegments = new Set<string>();
|
|
154
161
|
|
|
155
|
-
|
|
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
|
+
}
|
|
168
|
+
|
|
169
|
+
let rbsUpdate: MaybeThenable<boolean> = false;
|
|
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
|
+
}
|
|
156
175
|
|
|
157
|
-
return Promise.all([
|
|
158
|
-
|
|
159
|
-
segments.registerSegments(
|
|
160
|
-
]).then(([
|
|
176
|
+
return Promise.all([ffUpdate, rbsUpdate,
|
|
177
|
+
// @TODO if at least 1 segment fetch fails due to 404 and other segments are updated in the storage, SDK_UPDATE is not emitted
|
|
178
|
+
segments.registerSegments(setToArray(usedSegments))
|
|
179
|
+
]).then(([ffChanged, rbsChanged]) => {
|
|
161
180
|
if (splitsEventEmitter) {
|
|
162
181
|
// 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 || (
|
|
182
|
+
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
164
183
|
.catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
165
184
|
.then(emitSplitsArrivedEvent => {
|
|
166
185
|
// emit SDK events
|
|
@@ -177,7 +196,7 @@ export function splitChangesUpdaterFactory(
|
|
|
177
196
|
if (startingUp && retriesOnFailureBeforeReady > retry) {
|
|
178
197
|
retry += 1;
|
|
179
198
|
log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
|
|
180
|
-
return _splitChangesUpdater(
|
|
199
|
+
return _splitChangesUpdater(sinces, retry);
|
|
181
200
|
} else {
|
|
182
201
|
startingUp = false;
|
|
183
202
|
}
|
|
@@ -185,7 +204,7 @@ export function splitChangesUpdaterFactory(
|
|
|
185
204
|
});
|
|
186
205
|
}
|
|
187
206
|
|
|
188
|
-
|
|
189
|
-
return
|
|
207
|
+
// `getChangeNumber` never rejects or throws error
|
|
208
|
+
return Promise.all([splits.getChangeNumber(), rbSegments.getChangeNumber()]).then(_splitChangesUpdater);
|
|
190
209
|
};
|
|
191
210
|
}
|
|
@@ -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';
|