@splitsoftware/splitio-commons 2.1.0 → 2.1.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/README.md +1 -0
- 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 +7 -7
- package/cjs/evaluator/matchers/index.js +3 -1
- package/cjs/evaluator/matchers/matcherTypes.js +1 -0
- package/cjs/evaluator/matchers/rbsegment.js +43 -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 -6
- package/cjs/logger/messages/debug.js +3 -4
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/services/splitApi.js +2 -2
- package/cjs/storages/AbstractSplitsCacheAsync.js +12 -1
- package/cjs/storages/AbstractSplitsCacheSync.js +10 -9
- package/cjs/storages/KeyBuilder.js +8 -15
- package/cjs/storages/KeyBuilderCS.js +12 -3
- package/cjs/storages/KeyBuilderSS.js +3 -0
- package/cjs/storages/dataLoader.js +1 -2
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +117 -0
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +14 -16
- package/cjs/storages/inLocalStorage/index.js +4 -0
- 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 +24 -31
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +64 -0
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +4 -21
- package/cjs/storages/inRedis/index.js +2 -0
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +64 -0
- package/cjs/storages/pluggable/SplitsCachePluggable.js +2 -19
- package/cjs/storages/pluggable/index.js +2 -0
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +12 -13
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +2 -2
- 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 +53 -51
- 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/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 +7 -7
- package/esm/evaluator/matchers/index.js +3 -1
- package/esm/evaluator/matchers/matcherTypes.js +1 -0
- package/esm/evaluator/matchers/rbsegment.js +39 -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 -3
- package/esm/logger/messages/debug.js +3 -4
- package/esm/logger/messages/warn.js +1 -1
- package/esm/services/splitApi.js +2 -2
- package/esm/storages/AbstractSplitsCacheAsync.js +12 -1
- package/esm/storages/AbstractSplitsCacheSync.js +10 -9
- package/esm/storages/KeyBuilder.js +8 -15
- package/esm/storages/KeyBuilderCS.js +12 -3
- package/esm/storages/KeyBuilderSS.js +3 -0
- package/esm/storages/dataLoader.js +1 -2
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +114 -0
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +14 -16
- package/esm/storages/inLocalStorage/index.js +4 -0
- 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 +24 -31
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +61 -0
- package/esm/storages/inRedis/SplitsCacheInRedis.js +4 -21
- package/esm/storages/inRedis/index.js +2 -0
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +61 -0
- package/esm/storages/pluggable/SplitsCachePluggable.js +2 -19
- package/esm/storages/pluggable/index.js +2 -0
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +12 -13
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +2 -2
- 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 +53 -51
- 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/package.json +1 -1
- package/src/dtos/types.ts +32 -8
- package/src/evaluator/Engine.ts +1 -1
- 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 +7 -7
- package/src/evaluator/matchers/index.ts +3 -1
- package/src/evaluator/matchers/matcherTypes.ts +1 -0
- package/src/evaluator/matchers/rbsegment.ts +61 -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 -3
- package/src/logger/messages/debug.ts +3 -4
- package/src/logger/messages/warn.ts +1 -1
- 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 +15 -5
- package/src/storages/AbstractSplitsCacheSync.ts +14 -15
- package/src/storages/KeyBuilder.ts +9 -17
- package/src/storages/KeyBuilderCS.ts +15 -4
- package/src/storages/KeyBuilderSS.ts +4 -0
- package/src/storages/dataLoader.ts +1 -2
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +136 -0
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +15 -16
- package/src/storages/inLocalStorage/index.ts +4 -0
- 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 +22 -27
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +79 -0
- package/src/storages/inRedis/SplitsCacheInRedis.ts +4 -21
- package/src/storages/inRedis/index.ts +2 -0
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +76 -0
- package/src/storages/pluggable/SplitsCachePluggable.ts +2 -19
- package/src/storages/pluggable/index.ts +2 -0
- package/src/storages/types.ts +43 -17
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +14 -15
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +2 -1
- 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 -2
- 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 +64 -62
- 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/lang/index.ts +1 -1
package/src/storages/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import SplitIO from '../../types/splitio';
|
|
2
|
-
import { MaybeThenable, ISplit, IMySegmentsResponse } from '../dtos/types';
|
|
2
|
+
import { MaybeThenable, ISplit, IRBSegment, 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';
|
|
@@ -177,11 +177,9 @@ export interface IPluggableStorageWrapper {
|
|
|
177
177
|
/** Splits cache */
|
|
178
178
|
|
|
179
179
|
export interface ISplitsCacheBase {
|
|
180
|
-
|
|
181
|
-
removeSplits(names: string[]): MaybeThenable<boolean[] | void>,
|
|
180
|
+
update(toAdd: ISplit[], toRemove: ISplit[], changeNumber: number): MaybeThenable<boolean>,
|
|
182
181
|
getSplit(name: string): MaybeThenable<ISplit | null>,
|
|
183
182
|
getSplits(names: string[]): MaybeThenable<Record<string, ISplit | null>>, // `fetchMany` in spec
|
|
184
|
-
setChangeNumber(changeNumber: number): MaybeThenable<boolean | void>,
|
|
185
183
|
// should never reject or throw an exception. Instead return -1 by default, assuming no splits are present in the storage.
|
|
186
184
|
getChangeNumber(): MaybeThenable<number>,
|
|
187
185
|
getAll(): MaybeThenable<ISplit[]>,
|
|
@@ -198,11 +196,9 @@ export interface ISplitsCacheBase {
|
|
|
198
196
|
}
|
|
199
197
|
|
|
200
198
|
export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
201
|
-
|
|
202
|
-
removeSplits(names: string[]): boolean[],
|
|
199
|
+
update(toAdd: ISplit[], toRemove: ISplit[], changeNumber: number): boolean,
|
|
203
200
|
getSplit(name: string): ISplit | null,
|
|
204
201
|
getSplits(names: string[]): Record<string, ISplit | null>,
|
|
205
|
-
setChangeNumber(changeNumber: number): boolean | void,
|
|
206
202
|
getChangeNumber(): number,
|
|
207
203
|
getAll(): ISplit[],
|
|
208
204
|
getSplitNames(): string[],
|
|
@@ -215,11 +211,9 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
215
211
|
}
|
|
216
212
|
|
|
217
213
|
export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
218
|
-
|
|
219
|
-
removeSplits(names: string[]): Promise<boolean[] | void>,
|
|
214
|
+
update(toAdd: ISplit[], toRemove: ISplit[], changeNumber: number): Promise<boolean>,
|
|
220
215
|
getSplit(name: string): Promise<ISplit | null>,
|
|
221
216
|
getSplits(names: string[]): Promise<Record<string, ISplit | null>>,
|
|
222
|
-
setChangeNumber(changeNumber: number): Promise<boolean | void>,
|
|
223
217
|
getChangeNumber(): Promise<number>,
|
|
224
218
|
getAll(): Promise<ISplit[]>,
|
|
225
219
|
getSplitNames(): Promise<string[]>,
|
|
@@ -231,6 +225,34 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
|
231
225
|
getNamesByFlagSets(flagSets: string[]): Promise<Set<string>[]>
|
|
232
226
|
}
|
|
233
227
|
|
|
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
|
+
|
|
234
256
|
/** Segments cache */
|
|
235
257
|
|
|
236
258
|
export interface ISegmentsCacheBase {
|
|
@@ -428,15 +450,17 @@ export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync,
|
|
|
428
450
|
*/
|
|
429
451
|
|
|
430
452
|
export interface IStorageBase<
|
|
431
|
-
TSplitsCache extends ISplitsCacheBase,
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
453
|
+
TSplitsCache extends ISplitsCacheBase = ISplitsCacheBase,
|
|
454
|
+
TRBSegmentsCache extends IRBSegmentsCacheBase = IRBSegmentsCacheBase,
|
|
455
|
+
TSegmentsCache extends ISegmentsCacheBase = ISegmentsCacheBase,
|
|
456
|
+
TImpressionsCache extends IImpressionsCacheBase = IImpressionsCacheBase,
|
|
457
|
+
TImpressionsCountCache extends IImpressionCountsCacheBase = IImpressionCountsCacheBase,
|
|
458
|
+
TEventsCache extends IEventsCacheBase = IEventsCacheBase,
|
|
459
|
+
TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync = ITelemetryCacheSync | ITelemetryCacheAsync,
|
|
460
|
+
TUniqueKeysCache extends IUniqueKeysCacheBase = IUniqueKeysCacheBase
|
|
438
461
|
> {
|
|
439
462
|
splits: TSplitsCache,
|
|
463
|
+
rbSegments: TRBSegmentsCache,
|
|
440
464
|
segments: TSegmentsCache,
|
|
441
465
|
impressions: TImpressionsCache,
|
|
442
466
|
impressionCounts: TImpressionsCountCache,
|
|
@@ -449,6 +473,7 @@ export interface IStorageBase<
|
|
|
449
473
|
|
|
450
474
|
export interface IStorageSync extends IStorageBase<
|
|
451
475
|
ISplitsCacheSync,
|
|
476
|
+
IRBSegmentsCacheSync,
|
|
452
477
|
ISegmentsCacheSync,
|
|
453
478
|
IImpressionsCacheSync,
|
|
454
479
|
IImpressionCountsCacheSync,
|
|
@@ -462,6 +487,7 @@ export interface IStorageSync extends IStorageBase<
|
|
|
462
487
|
|
|
463
488
|
export interface IStorageAsync extends IStorageBase<
|
|
464
489
|
ISplitsCacheAsync,
|
|
490
|
+
IRBSegmentsCacheAsync,
|
|
465
491
|
ISegmentsCacheAsync,
|
|
466
492
|
IImpressionsCacheAsync | IImpressionsCacheSync,
|
|
467
493
|
IImpressionCountsCacheBase,
|
|
@@ -24,7 +24,7 @@ export function fromObjectUpdaterFactory(
|
|
|
24
24
|
let startingUp = true;
|
|
25
25
|
|
|
26
26
|
return function objectUpdater() {
|
|
27
|
-
const splits:
|
|
27
|
+
const splits: ISplit[] = [];
|
|
28
28
|
let loadError = null;
|
|
29
29
|
let splitsMock: false | Record<string, ISplitPartial> = {};
|
|
30
30
|
try {
|
|
@@ -37,24 +37,23 @@ export function fromObjectUpdaterFactory(
|
|
|
37
37
|
if (!loadError && splitsMock) {
|
|
38
38
|
log.debug(SYNC_OFFLINE_DATA, [JSON.stringify(splitsMock)]);
|
|
39
39
|
|
|
40
|
-
forOwn(splitsMock,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
]);
|
|
40
|
+
forOwn(splitsMock, (val, name) => {
|
|
41
|
+
// @ts-ignore Split changeNumber and seed is undefined in localhost mode
|
|
42
|
+
splits.push({
|
|
43
|
+
name,
|
|
44
|
+
status: 'ACTIVE',
|
|
45
|
+
killed: false,
|
|
46
|
+
trafficAllocation: 100,
|
|
47
|
+
defaultTreatment: CONTROL,
|
|
48
|
+
conditions: val.conditions || [],
|
|
49
|
+
configurations: val.configurations,
|
|
50
|
+
trafficTypeName: val.trafficTypeName
|
|
51
|
+
});
|
|
53
52
|
});
|
|
54
53
|
|
|
55
54
|
return Promise.all([
|
|
56
55
|
splitsCache.clear(), // required to sync removed splits from mock
|
|
57
|
-
splitsCache.
|
|
56
|
+
splitsCache.update(splits, [], Date.now())
|
|
58
57
|
]).then(() => {
|
|
59
58
|
readiness.splits.emit(SDK_SPLITS_ARRIVED);
|
|
60
59
|
|
|
@@ -11,11 +11,12 @@ export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges
|
|
|
11
11
|
since: number,
|
|
12
12
|
noCache?: boolean,
|
|
13
13
|
till?: number,
|
|
14
|
+
rbSince?: number,
|
|
14
15
|
// Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
|
|
15
16
|
decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
|
|
16
17
|
) {
|
|
17
18
|
|
|
18
|
-
let splitsPromise = fetchSplitChanges(since, noCache, till);
|
|
19
|
+
let splitsPromise = fetchSplitChanges(since, noCache, till, rbSince);
|
|
19
20
|
if (decorator) splitsPromise = decorator(splitsPromise);
|
|
20
21
|
|
|
21
22
|
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 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)
|
|
@@ -22,8 +22,7 @@ export function splitsSyncTaskFactory(
|
|
|
22
22
|
splitChangesUpdaterFactory(
|
|
23
23
|
settings.log,
|
|
24
24
|
splitChangesFetcherFactory(fetchSplitChanges),
|
|
25
|
-
storage
|
|
26
|
-
storage.segments,
|
|
25
|
+
storage,
|
|
27
26
|
settings.sync.__splitFiltersValidation,
|
|
28
27
|
readiness.splits,
|
|
29
28
|
settings.startup.requestTimeoutBeforeReady,
|
|
@@ -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
|
-
import { ISegmentsCacheBase,
|
|
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, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
|
|
7
7
|
import { ILogger } from '../../../logger/types';
|
|
8
|
-
import { SYNC_SPLITS_FETCH,
|
|
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.name, 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
|
-
|
|
115
|
-
segments: ISegmentsCacheBase,
|
|
112
|
+
storage: Pick<IStorageBase, 'splits' | 'rbSegments' | 'segments'>,
|
|
116
113
|
splitFiltersValidation: ISplitFiltersValidation,
|
|
117
114
|
splitsEventEmitter?: ISplitsEventEmitter,
|
|
118
115
|
requestTimeoutBeforeReady: number = 0,
|
|
119
116
|
retriesOnFailureBeforeReady: number = 0,
|
|
120
117
|
isClientSide?: boolean
|
|
121
|
-
):
|
|
118
|
+
): SplitChangesUpdater {
|
|
119
|
+
const { splits, rbSegments, segments } = storage;
|
|
122
120
|
|
|
123
121
|
let startingUp = true;
|
|
124
122
|
|
|
@@ -128,16 +126,6 @@ export function splitChangesUpdaterFactory(
|
|
|
128
126
|
return promise;
|
|
129
127
|
}
|
|
130
128
|
|
|
131
|
-
/** Returns true if at least one split was updated */
|
|
132
|
-
function isThereUpdate(flagsChange: [boolean | void, void | boolean[], void | boolean[], boolean | void] | [any, any, any]) {
|
|
133
|
-
const [, added, removed] = flagsChange;
|
|
134
|
-
// There is at least one added or modified feature flag
|
|
135
|
-
if (added && added.some((update: boolean) => update)) return true;
|
|
136
|
-
// There is at least one removed feature flag
|
|
137
|
-
if (removed && removed.some((update: boolean) => update)) return true;
|
|
138
|
-
return false;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
129
|
/**
|
|
142
130
|
* SplitChanges updater returns a promise that resolves with a `false` boolean value if it fails to fetch splits or synchronize them with the storage.
|
|
143
131
|
* Returned promise will not be rejected.
|
|
@@ -145,39 +133,53 @@ export function splitChangesUpdaterFactory(
|
|
|
145
133
|
* @param noCache - true to revalidate data to fetch
|
|
146
134
|
* @param till - query param to bypass CDN requests
|
|
147
135
|
*/
|
|
148
|
-
return function splitChangesUpdater(noCache?: boolean, till?: number,
|
|
136
|
+
return function splitChangesUpdater(noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) {
|
|
149
137
|
|
|
150
138
|
/**
|
|
151
139
|
* @param since - current changeNumber at splitsCache
|
|
152
140
|
* @param retry - current number of retry attempts
|
|
153
141
|
*/
|
|
154
|
-
function _splitChangesUpdater(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
142
|
+
function _splitChangesUpdater(sinces: [number, number], retry = 0): Promise<boolean> {
|
|
143
|
+
const [since, rbSince] = sinces;
|
|
144
|
+
log.debug(SYNC_SPLITS_FETCH, sinces);
|
|
145
|
+
const fetcherPromise = 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)
|
|
159
156
|
)
|
|
160
157
|
.then((splitChanges: ISplitChangesResponse) => {
|
|
161
158
|
startingUp = false;
|
|
162
159
|
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
160
|
+
const usedSegments = new Set<string>();
|
|
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
|
+
}
|
|
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
|
+
}
|
|
175
|
+
|
|
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]) => {
|
|
178
180
|
if (splitsEventEmitter) {
|
|
179
181
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
180
|
-
return Promise.resolve(!splitsEventEmitter.splitsArrived || (
|
|
182
|
+
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
181
183
|
.catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
182
184
|
.then(emitSplitsArrivedEvent => {
|
|
183
185
|
// emit SDK events
|
|
@@ -194,7 +196,7 @@ export function splitChangesUpdaterFactory(
|
|
|
194
196
|
if (startingUp && retriesOnFailureBeforeReady > retry) {
|
|
195
197
|
retry += 1;
|
|
196
198
|
log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
|
|
197
|
-
return _splitChangesUpdater(
|
|
199
|
+
return _splitChangesUpdater(sinces, retry);
|
|
198
200
|
} else {
|
|
199
201
|
startingUp = false;
|
|
200
202
|
}
|
|
@@ -211,7 +213,7 @@ export function splitChangesUpdaterFactory(
|
|
|
211
213
|
return fetcherPromise;
|
|
212
214
|
}
|
|
213
215
|
|
|
214
|
-
|
|
215
|
-
return
|
|
216
|
+
// `getChangeNumber` never rejects or throws error
|
|
217
|
+
return Promise.all([splits.getChangeNumber(), rbSegments.getChangeNumber()]).then(_splitChangesUpdater);
|
|
216
218
|
};
|
|
217
219
|
}
|
|
@@ -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,
|