@splitsoftware/splitio-commons 1.16.1-rc.1 → 1.16.1-rc.10
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/cjs/logger/constants.js +5 -4
- package/cjs/logger/messages/info.js +2 -1
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/readiness/readinessManager.js +7 -12
- package/cjs/services/splitApi.js +4 -8
- package/cjs/storages/AbstractSegmentsCacheSync.js +1 -6
- package/cjs/storages/AbstractSplitsCacheAsync.js +2 -2
- package/cjs/storages/AbstractSplitsCacheSync.js +7 -5
- package/cjs/storages/KeyBuilder.js +0 -3
- package/cjs/storages/KeyBuilderCS.js +6 -0
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +23 -2
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +4 -16
- package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +5 -1
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +6 -15
- package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +4 -11
- package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
- package/cjs/sync/polling/pollingManagerCS.js +33 -51
- package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +23 -19
- package/cjs/sync/streaming/SSEHandler/index.js +7 -8
- package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +8 -4
- package/cjs/sync/streaming/constants.js +2 -3
- package/cjs/sync/streaming/parseUtils.js +14 -9
- package/cjs/sync/streaming/pushManager.js +29 -52
- package/cjs/sync/submitters/telemetrySubmitter.js +0 -2
- package/cjs/sync/syncManagerOnline.js +14 -24
- package/cjs/utils/constants/index.js +1 -1
- package/cjs/utils/settingsValidation/index.js +1 -5
- package/esm/logger/constants.js +2 -1
- package/esm/logger/messages/info.js +2 -1
- package/esm/logger/messages/warn.js +1 -1
- package/esm/readiness/readinessManager.js +7 -12
- package/esm/services/splitApi.js +5 -9
- package/esm/storages/AbstractSegmentsCacheSync.js +1 -6
- package/esm/storages/AbstractSplitsCacheAsync.js +2 -2
- package/esm/storages/AbstractSplitsCacheSync.js +5 -3
- package/esm/storages/KeyBuilder.js +0 -3
- package/esm/storages/KeyBuilderCS.js +6 -0
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +23 -2
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -17
- package/esm/storages/inMemory/MySegmentsCacheInMemory.js +5 -1
- package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -16
- package/esm/sync/polling/fetchers/mySegmentsFetcher.js +4 -11
- package/esm/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
- package/esm/sync/polling/pollingManagerCS.js +34 -52
- package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +21 -17
- package/esm/sync/streaming/SSEHandler/index.js +8 -9
- package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +8 -4
- package/esm/sync/streaming/constants.js +1 -2
- package/esm/sync/streaming/parseUtils.js +12 -8
- package/esm/sync/streaming/pushManager.js +31 -53
- package/esm/sync/submitters/telemetrySubmitter.js +0 -2
- package/esm/sync/syncManagerOnline.js +15 -25
- package/esm/utils/constants/index.js +1 -1
- package/esm/utils/settingsValidation/index.js +1 -5
- package/package.json +1 -1
- package/src/dtos/types.ts +14 -12
- package/src/logger/constants.ts +2 -1
- package/src/logger/messages/info.ts +2 -1
- package/src/logger/messages/warn.ts +1 -1
- package/src/readiness/readinessManager.ts +7 -9
- package/src/readiness/types.ts +0 -1
- package/src/services/splitApi.ts +6 -11
- package/src/services/splitHttpClient.ts +1 -1
- package/src/services/types.ts +2 -3
- package/src/storages/AbstractSegmentsCacheSync.ts +2 -6
- package/src/storages/AbstractSplitsCacheAsync.ts +2 -2
- package/src/storages/AbstractSplitsCacheSync.ts +6 -4
- package/src/storages/KeyBuilder.ts +0 -3
- package/src/storages/KeyBuilderCS.ts +9 -0
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +26 -2
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +5 -20
- package/src/storages/inMemory/MySegmentsCacheInMemory.ts +7 -1
- package/src/storages/inMemory/SplitsCacheInMemory.ts +7 -13
- package/src/storages/types.ts +6 -5
- package/src/sync/polling/fetchers/mySegmentsFetcher.ts +7 -14
- package/src/sync/polling/fetchers/segmentChangesFetcher.ts +1 -1
- package/src/sync/polling/fetchers/types.ts +2 -2
- package/src/sync/polling/pollingManagerCS.ts +29 -61
- package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +12 -13
- package/src/sync/polling/types.ts +8 -7
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +20 -16
- package/src/sync/streaming/SSEClient/index.ts +4 -6
- package/src/sync/streaming/SSEHandler/index.ts +11 -13
- package/src/sync/streaming/SSEHandler/types.ts +13 -25
- package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +11 -7
- package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +1 -1
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +1 -1
- package/src/sync/streaming/UpdateWorkers/types.ts +2 -2
- package/src/sync/streaming/constants.ts +1 -2
- package/src/sync/streaming/parseUtils.ts +19 -11
- package/src/sync/streaming/pushManager.ts +37 -64
- package/src/sync/streaming/types.ts +9 -11
- package/src/sync/submitters/telemetrySubmitter.ts +0 -2
- package/src/sync/submitters/types.ts +1 -3
- package/src/sync/syncManagerOnline.ts +11 -19
- package/src/types.ts +1 -26
- package/src/utils/constants/index.ts +1 -1
- package/src/utils/settingsValidation/index.ts +1 -5
- package/types/dtos/types.d.ts +14 -11
- package/types/logger/constants.d.ts +2 -1
- package/types/readiness/types.d.ts +0 -1
- package/types/services/decorateHeaders.d.ts +2 -0
- package/types/services/splitApi.d.ts +1 -1
- package/types/services/splitHttpClient.d.ts +1 -1
- package/types/services/types.d.ts +2 -3
- package/types/storages/AbstractSegmentsCacheSync.d.ts +2 -6
- package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
- package/types/storages/AbstractSplitsCacheSync.d.ts +3 -3
- package/types/storages/KeyBuilder.d.ts +0 -1
- package/types/storages/KeyBuilderCS.d.ts +2 -0
- package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +3 -2
- package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +1 -1
- package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +3 -1
- package/types/storages/inMemory/SplitsCacheInMemory.d.ts +1 -2
- package/types/storages/types.d.ts +4 -4
- package/types/sync/polling/fetchers/mySegmentsFetcher.d.ts +2 -2
- package/types/sync/polling/fetchers/types.d.ts +2 -2
- package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +4 -3
- package/types/sync/polling/types.d.ts +6 -12
- package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +3 -2
- package/types/sync/streaming/SSEHandler/types.d.ts +13 -22
- package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +2 -2
- package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
- package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
- package/types/sync/streaming/UpdateWorkers/types.d.ts +2 -2
- package/types/sync/streaming/constants.d.ts +1 -2
- package/types/sync/streaming/parseUtils.d.ts +4 -5
- package/types/sync/streaming/pushManager.d.ts +0 -2
- package/types/sync/streaming/pushManagerCS_Spec1_3.d.ts +9 -0
- package/types/sync/streaming/pushManager_Spec1_3.d.ts +9 -0
- package/types/sync/streaming/types.d.ts +8 -9
- package/types/sync/submitters/types.d.ts +1 -3
- package/types/types.d.ts +0 -25
- package/types/utils/constants/index.d.ts +1 -1
- package/types/utils/settingsValidation/index.d.ts +0 -2
|
@@ -2,6 +2,7 @@ import { ISplitsCacheSync } from './types';
|
|
|
2
2
|
import { ISplit } from '../dtos/types';
|
|
3
3
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
4
4
|
import { ISet } from '../utils/lang/sets';
|
|
5
|
+
import { IN_SEGMENT, IN_LARGE_SEGMENT } from '../utils/constants';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* This class provides a skeletal implementation of the ISplitsCacheSync interface
|
|
@@ -43,7 +44,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
|
|
|
43
44
|
|
|
44
45
|
abstract trafficTypeExists(trafficType: string): boolean
|
|
45
46
|
|
|
46
|
-
abstract
|
|
47
|
+
abstract usesSegments(): boolean
|
|
47
48
|
|
|
48
49
|
abstract clear(): void
|
|
49
50
|
|
|
@@ -85,15 +86,16 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
|
|
|
85
86
|
|
|
86
87
|
/**
|
|
87
88
|
* Given a parsed split, it returns a boolean flagging if its conditions use segments matchers (rules & whitelists).
|
|
88
|
-
* This util is intended to simplify the implementation of `splitsCache::
|
|
89
|
+
* This util is intended to simplify the implementation of `splitsCache::usesSegments` method
|
|
89
90
|
*/
|
|
90
|
-
export function
|
|
91
|
+
export function usesSegments(split: ISplit) {
|
|
91
92
|
const conditions = split.conditions || [];
|
|
92
93
|
for (let i = 0; i < conditions.length; i++) {
|
|
93
94
|
const matchers = conditions[i].matcherGroup.matchers;
|
|
94
95
|
|
|
95
96
|
for (let j = 0; j < matchers.length; j++) {
|
|
96
|
-
|
|
97
|
+
const matcher = matchers[j].matcherType;
|
|
98
|
+
if (matcher === IN_SEGMENT || matcher === IN_LARGE_SEGMENT) return true;
|
|
97
99
|
}
|
|
98
100
|
}
|
|
99
101
|
|
|
@@ -51,9 +51,6 @@ export class KeyBuilder {
|
|
|
51
51
|
buildSplitsWithSegmentCountKey() {
|
|
52
52
|
return `${this.prefix}.splits.usingSegments`;
|
|
53
53
|
}
|
|
54
|
-
buildSplitsWithLargeSegmentCountKey() {
|
|
55
|
-
return `${this.prefix}.splits.usingLargeSegments`;
|
|
56
|
-
}
|
|
57
54
|
|
|
58
55
|
buildSegmentNameKey(segmentName: string) {
|
|
59
56
|
return `${this.prefix}.segment.${segmentName}`;
|
|
@@ -5,6 +5,7 @@ export interface MySegmentsKeyBuilder {
|
|
|
5
5
|
buildSegmentNameKey(segmentName: string): string;
|
|
6
6
|
extractSegmentName(builtSegmentKeyName: string): string | undefined;
|
|
7
7
|
extractOldSegmentKey(builtSegmentKeyName: string): string | undefined;
|
|
8
|
+
buildTillKey(): string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
|
|
@@ -47,6 +48,10 @@ export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
|
|
|
47
48
|
isSplitsCacheKey(key: string) {
|
|
48
49
|
return this.regexSplitsCacheKey.test(key);
|
|
49
50
|
}
|
|
51
|
+
|
|
52
|
+
buildTillKey() {
|
|
53
|
+
return `${this.prefix}.${this.matchingKey}.segments.till`;
|
|
54
|
+
}
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
export function myLargeSegmentsKeyBuilder(prefix: string, matchingKey: string): MySegmentsKeyBuilder {
|
|
@@ -63,6 +68,10 @@ export function myLargeSegmentsKeyBuilder(prefix: string, matchingKey: string):
|
|
|
63
68
|
|
|
64
69
|
extractOldSegmentKey() {
|
|
65
70
|
return undefined;
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
buildTillKey() {
|
|
74
|
+
return `${prefix}.${matchingKey}.largeSegments.till`;
|
|
66
75
|
}
|
|
67
76
|
};
|
|
68
77
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ILogger } from '../../logger/types';
|
|
2
|
+
import { isNaNNumber } from '../../utils/lang';
|
|
2
3
|
import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
|
|
3
4
|
import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
|
|
4
5
|
import { LOG_PREFIX, DEFINED } from './constants';
|
|
@@ -58,10 +59,20 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
|
|
|
58
59
|
/**
|
|
59
60
|
* Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
|
|
60
61
|
*
|
|
61
|
-
* @param {string[]}
|
|
62
|
+
* @param {string[]} names list of segment names
|
|
62
63
|
* @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
|
|
63
64
|
*/
|
|
64
|
-
resetSegments(names: string[]): boolean {
|
|
65
|
+
resetSegments(names: string[], changeNumber?: number): boolean {
|
|
66
|
+
try {
|
|
67
|
+
if (changeNumber) {
|
|
68
|
+
localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
|
|
69
|
+
} else {
|
|
70
|
+
localStorage.removeItem(this.keys.buildTillKey());
|
|
71
|
+
}
|
|
72
|
+
} catch (e) {
|
|
73
|
+
this.log.error(e);
|
|
74
|
+
}
|
|
75
|
+
|
|
65
76
|
let isDiff = false;
|
|
66
77
|
let index;
|
|
67
78
|
|
|
@@ -133,4 +144,17 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
|
|
|
133
144
|
return 1;
|
|
134
145
|
}
|
|
135
146
|
|
|
147
|
+
getChangeNumber() {
|
|
148
|
+
const n = -1;
|
|
149
|
+
let value: string | number | null = localStorage.getItem(this.keys.buildTillKey());
|
|
150
|
+
|
|
151
|
+
if (value !== null) {
|
|
152
|
+
value = parseInt(value, 10);
|
|
153
|
+
|
|
154
|
+
return isNaNNumber(value) ? n : value;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return n;
|
|
158
|
+
}
|
|
159
|
+
|
|
136
160
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ISplit } from '../../dtos/types';
|
|
2
|
-
import { AbstractSplitsCacheSync,
|
|
2
|
+
import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
|
|
3
3
|
import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
|
|
4
4
|
import { KeyBuilderCS } from '../KeyBuilderCS';
|
|
5
5
|
import { ILogger } from '../../logger/types';
|
|
@@ -7,7 +7,6 @@ import { LOG_PREFIX } from './constants';
|
|
|
7
7
|
import { ISet, _Set, setToArray } from '../../utils/lang/sets';
|
|
8
8
|
import { ISettings } from '../../types';
|
|
9
9
|
import { getStorageHash } from '../KeyBuilder';
|
|
10
|
-
import { IN_LARGE_SEGMENT, IN_SEGMENT } from '../../utils/constants';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
|
|
@@ -51,15 +50,10 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
51
50
|
const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
52
51
|
this._decrementCount(ttKey);
|
|
53
52
|
|
|
54
|
-
if (
|
|
53
|
+
if (usesSegments(split)) {
|
|
55
54
|
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
56
55
|
this._decrementCount(segmentsCountKey);
|
|
57
56
|
}
|
|
58
|
-
|
|
59
|
-
if (usesMatcher(split, IN_LARGE_SEGMENT)) {
|
|
60
|
-
const segmentsCountKey = this.keys.buildSplitsWithLargeSegmentCountKey();
|
|
61
|
-
this._decrementCount(segmentsCountKey);
|
|
62
|
-
}
|
|
63
57
|
}
|
|
64
58
|
} catch (e) {
|
|
65
59
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -73,17 +67,11 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
73
67
|
// @ts-expect-error
|
|
74
68
|
localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
|
|
75
69
|
|
|
76
|
-
if (
|
|
70
|
+
if (usesSegments(split)) {
|
|
77
71
|
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
78
72
|
// @ts-expect-error
|
|
79
73
|
localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
|
|
80
74
|
}
|
|
81
|
-
|
|
82
|
-
if (usesMatcher(split, IN_LARGE_SEGMENT)) {
|
|
83
|
-
const segmentsCountKey = this.keys.buildSplitsWithLargeSegmentCountKey();
|
|
84
|
-
// @ts-expect-error
|
|
85
|
-
localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
|
|
86
|
-
}
|
|
87
75
|
}
|
|
88
76
|
} catch (e) {
|
|
89
77
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -215,14 +203,11 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
215
203
|
return isFiniteNumber(ttCount) && ttCount > 0;
|
|
216
204
|
}
|
|
217
205
|
|
|
218
|
-
|
|
206
|
+
usesSegments() {
|
|
219
207
|
// If cache hasn't been synchronized with the cloud, assume we need them.
|
|
220
208
|
if (!this.hasSync) return true;
|
|
221
209
|
|
|
222
|
-
const storedCount = localStorage.getItem(
|
|
223
|
-
this.keys.buildSplitsWithSegmentCountKey() :
|
|
224
|
-
this.keys.buildSplitsWithLargeSegmentCountKey()
|
|
225
|
-
);
|
|
210
|
+
const storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
226
211
|
const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
|
|
227
212
|
|
|
228
213
|
if (isFiniteNumber(splitsWithSegmentsCount)) {
|
|
@@ -7,6 +7,7 @@ import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
|
|
|
7
7
|
export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
|
|
8
8
|
|
|
9
9
|
private segmentCache: Record<string, boolean> = {};
|
|
10
|
+
private cn?: number;
|
|
10
11
|
|
|
11
12
|
clear() {
|
|
12
13
|
this.segmentCache = {};
|
|
@@ -35,7 +36,8 @@ export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
|
|
|
35
36
|
* @param {string[]} names list of segment names
|
|
36
37
|
* @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
|
|
37
38
|
*/
|
|
38
|
-
resetSegments(names: string[]): boolean {
|
|
39
|
+
resetSegments(names: string[], changeNumber?: number): boolean {
|
|
40
|
+
this.cn = changeNumber;
|
|
39
41
|
let isDiff = false;
|
|
40
42
|
let index;
|
|
41
43
|
|
|
@@ -72,6 +74,10 @@ export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
|
|
|
72
74
|
return isDiff;
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
getChangeNumber() {
|
|
78
|
+
return this.cn || -1;
|
|
79
|
+
}
|
|
80
|
+
|
|
75
81
|
getRegisteredSegments() {
|
|
76
82
|
return Object.keys(this.segmentCache);
|
|
77
83
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
|
|
2
|
-
import { AbstractSplitsCacheSync,
|
|
2
|
+
import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
|
|
3
3
|
import { isFiniteNumber } from '../../utils/lang';
|
|
4
4
|
import { ISet, _Set } from '../../utils/lang/sets';
|
|
5
|
-
import { IN_LARGE_SEGMENT, IN_SEGMENT } from '../../utils/constants';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Default ISplitsCacheSync implementation that stores split definitions in memory.
|
|
@@ -15,7 +14,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
15
14
|
private ttCache: Record<string, number> = {};
|
|
16
15
|
private changeNumber: number = -1;
|
|
17
16
|
private segmentsCount: number = 0;
|
|
18
|
-
private largeSegmentsCount: number = 0;
|
|
19
17
|
private flagSetsCache: Record<string, ISet<string>> = {};
|
|
20
18
|
|
|
21
19
|
constructor(splitFiltersValidation?: ISplitFiltersValidation) {
|
|
@@ -28,7 +26,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
28
26
|
this.ttCache = {};
|
|
29
27
|
this.changeNumber = -1;
|
|
30
28
|
this.segmentsCount = 0;
|
|
31
|
-
this.largeSegmentsCount = 0;
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
addSplit(name: string, split: ISplit): boolean {
|
|
@@ -41,9 +38,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
41
38
|
|
|
42
39
|
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
|
|
43
40
|
|
|
44
|
-
//
|
|
45
|
-
if (
|
|
46
|
-
if (usesMatcher(previousSplit, IN_LARGE_SEGMENT)) this.largeSegmentsCount--;
|
|
41
|
+
// Subtract from segments count for the previous version of this Split
|
|
42
|
+
if (usesSegments(previousSplit)) this.segmentsCount--;
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
if (split) {
|
|
@@ -55,8 +51,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
55
51
|
this.addToFlagSets(split);
|
|
56
52
|
|
|
57
53
|
// Add to segments count for the new version of the Split
|
|
58
|
-
if (
|
|
59
|
-
if (usesMatcher(split, IN_LARGE_SEGMENT)) this.largeSegmentsCount++;
|
|
54
|
+
if (usesSegments(split)) this.segmentsCount++;
|
|
60
55
|
|
|
61
56
|
return true;
|
|
62
57
|
} else {
|
|
@@ -76,8 +71,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
76
71
|
this.removeFromFlagSets(split.name, split.sets);
|
|
77
72
|
|
|
78
73
|
// Update the segments count.
|
|
79
|
-
if (
|
|
80
|
-
if (usesMatcher(split, IN_LARGE_SEGMENT)) this.largeSegmentsCount--;
|
|
74
|
+
if (usesSegments(split)) this.segmentsCount--;
|
|
81
75
|
|
|
82
76
|
return true;
|
|
83
77
|
} else {
|
|
@@ -106,8 +100,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
106
100
|
return isFiniteNumber(this.ttCache[trafficType]) && this.ttCache[trafficType] > 0;
|
|
107
101
|
}
|
|
108
102
|
|
|
109
|
-
|
|
110
|
-
return this.getChangeNumber() === -1 ||
|
|
103
|
+
usesSegments(): boolean {
|
|
104
|
+
return this.getChangeNumber() === -1 || this.segmentsCount > 0;
|
|
111
105
|
}
|
|
112
106
|
|
|
113
107
|
getNamesByFlagSets(flagSets: string[]): ISet<string>[] {
|
package/src/storages/types.ts
CHANGED
|
@@ -204,8 +204,8 @@ export interface ISplitsCacheBase {
|
|
|
204
204
|
getSplitNames(): MaybeThenable<string[]>,
|
|
205
205
|
// should never reject or throw an exception. Instead return true by default, asssuming the TT might exist.
|
|
206
206
|
trafficTypeExists(trafficType: string): MaybeThenable<boolean>,
|
|
207
|
-
// only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === 1) or contains a FF using
|
|
208
|
-
|
|
207
|
+
// only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === -1) or contains a FF using segments or large segments
|
|
208
|
+
usesSegments(): MaybeThenable<boolean>,
|
|
209
209
|
clear(): MaybeThenable<boolean | void>,
|
|
210
210
|
// should never reject or throw an exception. Instead return false by default, to avoid emitting SDK_READY_FROM_CACHE.
|
|
211
211
|
checkCache(): MaybeThenable<boolean>,
|
|
@@ -223,7 +223,7 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
223
223
|
getAll(): ISplit[],
|
|
224
224
|
getSplitNames(): string[],
|
|
225
225
|
trafficTypeExists(trafficType: string): boolean,
|
|
226
|
-
|
|
226
|
+
usesSegments(): boolean,
|
|
227
227
|
clear(): void,
|
|
228
228
|
checkCache(): boolean,
|
|
229
229
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
|
|
@@ -240,7 +240,7 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
|
240
240
|
getAll(): Promise<ISplit[]>,
|
|
241
241
|
getSplitNames(): Promise<string[]>,
|
|
242
242
|
trafficTypeExists(trafficType: string): Promise<boolean>,
|
|
243
|
-
|
|
243
|
+
usesSegments(): Promise<boolean>,
|
|
244
244
|
clear(): Promise<boolean | void>,
|
|
245
245
|
checkCache(): Promise<boolean>,
|
|
246
246
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
|
|
@@ -270,7 +270,7 @@ export interface ISegmentsCacheSync extends ISegmentsCacheBase {
|
|
|
270
270
|
getKeysCount(): number // only used for telemetry
|
|
271
271
|
setChangeNumber(name: string, changeNumber: number): boolean
|
|
272
272
|
getChangeNumber(name: string): number
|
|
273
|
-
resetSegments(names: string[]): boolean // only for Sync Client-Side
|
|
273
|
+
resetSegments(names: string[], changeNumber?: number): boolean // only for Sync Client-Side
|
|
274
274
|
clear(): void
|
|
275
275
|
}
|
|
276
276
|
|
|
@@ -478,6 +478,7 @@ export interface IStorageSync extends IStorageBase<
|
|
|
478
478
|
ITelemetryCacheSync,
|
|
479
479
|
IUniqueKeysCacheSync
|
|
480
480
|
> {
|
|
481
|
+
// Defined in client-side
|
|
481
482
|
largeSegments?: ISegmentsCacheSync,
|
|
482
483
|
}
|
|
483
484
|
|
|
@@ -1,31 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { IFetchMemberships, IResponse } from '../../../services/types';
|
|
2
|
+
import { IMembershipsResponse } from '../../../dtos/types';
|
|
3
3
|
import { IMySegmentsFetcher } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Factory of MySegments fetcher.
|
|
7
7
|
* MySegments fetcher is a wrapper around `mySegments` API service that parses the response and handle errors.
|
|
8
8
|
*/
|
|
9
|
-
export function mySegmentsFetcherFactory(
|
|
9
|
+
export function mySegmentsFetcherFactory(fetchMemberships: IFetchMemberships): IMySegmentsFetcher {
|
|
10
10
|
|
|
11
11
|
return function mySegmentsFetcher(
|
|
12
12
|
userMatchingKey: string,
|
|
13
13
|
noCache?: boolean,
|
|
14
|
-
// Optional decorator for `
|
|
14
|
+
// Optional decorator for `fetchMemberships` promise, such as timeout or time tracker
|
|
15
15
|
decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
|
|
16
|
-
): Promise<
|
|
16
|
+
): Promise<IMembershipsResponse> {
|
|
17
17
|
|
|
18
|
-
let mySegmentsPromise =
|
|
18
|
+
let mySegmentsPromise = fetchMemberships(userMatchingKey, noCache);
|
|
19
19
|
if (decorator) mySegmentsPromise = decorator(mySegmentsPromise);
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
return mySegmentsPromise
|
|
23
|
-
.then(resp => resp.json())
|
|
24
|
-
.then((json: IMySegmentsResponse | IMyLargeSegmentsResponse) => {
|
|
25
|
-
return (json as IMySegmentsResponse).mySegments ?
|
|
26
|
-
(json as IMySegmentsResponse).mySegments.map((segment) => segment.name) :
|
|
27
|
-
(json as IMyLargeSegmentsResponse).myLargeSegments;
|
|
28
|
-
});
|
|
21
|
+
return mySegmentsPromise.then(resp => resp.json());
|
|
29
22
|
};
|
|
30
23
|
|
|
31
24
|
}
|
|
@@ -28,7 +28,7 @@ export function segmentChangesFetcherFactory(fetchSegmentChanges: IFetchSegmentC
|
|
|
28
28
|
segmentName: string,
|
|
29
29
|
noCache?: boolean,
|
|
30
30
|
till?: number,
|
|
31
|
-
// Optional decorator for `
|
|
31
|
+
// Optional decorator for `fetchSegmentChanges` promise, such as timeout or time tracker
|
|
32
32
|
decorator?: (promise: Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>
|
|
33
33
|
): Promise<ISegmentChangesResponse[]> {
|
|
34
34
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ISplitChangesResponse, ISegmentChangesResponse } from '../../../dtos/types';
|
|
1
|
+
import { ISplitChangesResponse, ISegmentChangesResponse, IMembershipsResponse } from '../../../dtos/types';
|
|
2
2
|
import { IResponse } from '../../../services/types';
|
|
3
3
|
|
|
4
4
|
export type ISplitChangesFetcher = (
|
|
@@ -20,4 +20,4 @@ export type IMySegmentsFetcher = (
|
|
|
20
20
|
userMatchingKey: string,
|
|
21
21
|
noCache?: boolean,
|
|
22
22
|
decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
|
|
23
|
-
) => Promise<
|
|
23
|
+
) => Promise<IMembershipsResponse>
|
|
@@ -6,9 +6,8 @@ import { mySegmentsSyncTaskFactory } from './syncTasks/mySegmentsSyncTask';
|
|
|
6
6
|
import { splitsSyncTaskFactory } from './syncTasks/splitsSyncTask';
|
|
7
7
|
import { getMatching } from '../../utils/key';
|
|
8
8
|
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../../readiness/constants';
|
|
9
|
-
import { POLLING_START, POLLING_STOP } from '../../logger/constants';
|
|
9
|
+
import { POLLING_SMART_PAUSING, POLLING_START, POLLING_STOP } from '../../logger/constants';
|
|
10
10
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
11
|
-
import { IN_LARGE_SEGMENT, IN_SEGMENT } from '../../utils/constants';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Expose start / stop mechanism for polling data from services.
|
|
@@ -23,92 +22,62 @@ export function pollingManagerCSFactory(
|
|
|
23
22
|
|
|
24
23
|
const splitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings, true);
|
|
25
24
|
|
|
26
|
-
// Map of matching keys to their corresponding MySegmentsSyncTask
|
|
27
|
-
const mySegmentsSyncTasks: Record<string,
|
|
25
|
+
// Map of matching keys to their corresponding MySegmentsSyncTask.
|
|
26
|
+
const mySegmentsSyncTasks: Record<string, IMySegmentsSyncTask> = {};
|
|
28
27
|
|
|
29
28
|
const matchingKey = getMatching(settings.core.key);
|
|
30
|
-
const
|
|
29
|
+
const mySegmentsSyncTask = add(matchingKey, readiness, storage);
|
|
31
30
|
|
|
32
31
|
function startMySegmentsSyncTasks() {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
forOwn(mySegmentsSyncTasks, ({ msSyncTask, mlsSyncTask }) => {
|
|
37
|
-
if (splitsHaveSegments) msSyncTask.start();
|
|
38
|
-
else msSyncTask.stop(); // smart pausing
|
|
39
|
-
|
|
40
|
-
if (mlsSyncTask) {
|
|
41
|
-
if (splitsHaveLargeSegments) mlsSyncTask.start();
|
|
42
|
-
else mlsSyncTask.stop(); // smart pausing
|
|
43
|
-
}
|
|
32
|
+
forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
|
|
33
|
+
mySegmentsSyncTask.start();
|
|
44
34
|
});
|
|
45
35
|
}
|
|
46
36
|
|
|
47
37
|
function stopMySegmentsSyncTasks() {
|
|
48
|
-
forOwn(mySegmentsSyncTasks, (
|
|
49
|
-
|
|
50
|
-
mlsSyncTask && mlsSyncTask.stop();
|
|
38
|
+
forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
|
|
39
|
+
if (mySegmentsSyncTask.isRunning()) mySegmentsSyncTask.stop();
|
|
51
40
|
});
|
|
52
41
|
}
|
|
53
42
|
|
|
43
|
+
// smart pausing
|
|
54
44
|
readiness.splits.on(SDK_SPLITS_ARRIVED, () => {
|
|
55
|
-
if (splitsSyncTask.isRunning())
|
|
45
|
+
if (!splitsSyncTask.isRunning()) return; // noop if not doing polling
|
|
46
|
+
const splitsHaveSegments = storage.splits.usesSegments();
|
|
47
|
+
if (splitsHaveSegments !== mySegmentsSyncTask.isRunning()) {
|
|
48
|
+
log.info(POLLING_SMART_PAUSING, [splitsHaveSegments ? 'ON' : 'OFF']);
|
|
49
|
+
if (splitsHaveSegments) {
|
|
50
|
+
startMySegmentsSyncTasks();
|
|
51
|
+
} else {
|
|
52
|
+
stopMySegmentsSyncTasks();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
56
55
|
});
|
|
57
56
|
|
|
58
57
|
function add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync) {
|
|
59
|
-
const
|
|
60
|
-
splitApi.fetchMySegments,
|
|
61
|
-
storage.segments,
|
|
62
|
-
() => { if (storage.splits.usesMatcher(IN_SEGMENT)) readiness.segments.emit(SDK_SEGMENTS_ARRIVED); },
|
|
63
|
-
settings,
|
|
64
|
-
matchingKey,
|
|
65
|
-
settings.scheduler.segmentsRefreshRate,
|
|
66
|
-
'mySegmentsUpdater'
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
let mlsSyncTask;
|
|
70
|
-
if (settings.sync.largeSegmentsEnabled) {
|
|
71
|
-
mlsSyncTask = mySegmentsSyncTaskFactory(
|
|
72
|
-
splitApi.fetchMyLargeSegments,
|
|
73
|
-
storage.largeSegments!,
|
|
74
|
-
() => { if (readiness.largeSegments && storage.splits.usesMatcher(IN_LARGE_SEGMENT)) readiness.largeSegments.emit(SDK_SEGMENTS_ARRIVED); },
|
|
75
|
-
settings,
|
|
76
|
-
matchingKey,
|
|
77
|
-
settings.scheduler.largeSegmentsRefreshRate,
|
|
78
|
-
'myLargeSegmentsUpdater'
|
|
79
|
-
);
|
|
80
|
-
}
|
|
58
|
+
const mySegmentsSyncTask = mySegmentsSyncTaskFactory(splitApi.fetchMemberships, storage, readiness, settings, matchingKey);
|
|
81
59
|
|
|
82
60
|
// smart ready
|
|
83
61
|
function smartReady() {
|
|
84
|
-
if (!readiness.isReady())
|
|
85
|
-
if (readiness.largeSegments && !storage.splits.usesMatcher(IN_LARGE_SEGMENT)) readiness.largeSegments.emit(SDK_SEGMENTS_ARRIVED);
|
|
86
|
-
if (!storage.splits.usesMatcher(IN_SEGMENT)) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
87
|
-
}
|
|
62
|
+
if (!readiness.isReady() && !storage.splits.usesSegments()) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
88
63
|
}
|
|
64
|
+
if (!storage.splits.usesSegments()) setTimeout(smartReady, 0);
|
|
65
|
+
else readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady);
|
|
89
66
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
mySegmentsSyncTasks[matchingKey] = { msSyncTask: msSyncTask, mlsSyncTask: mlsSyncTask };
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
msSyncTask,
|
|
97
|
-
mlsSyncTask
|
|
98
|
-
};
|
|
67
|
+
mySegmentsSyncTasks[matchingKey] = mySegmentsSyncTask;
|
|
68
|
+
return mySegmentsSyncTask;
|
|
99
69
|
}
|
|
100
70
|
|
|
101
71
|
return {
|
|
102
72
|
splitsSyncTask,
|
|
103
|
-
segmentsSyncTask:
|
|
104
|
-
largeSegmentsSyncTask: mlsSyncTask,
|
|
73
|
+
segmentsSyncTask: mySegmentsSyncTask,
|
|
105
74
|
|
|
106
75
|
// Start periodic fetching (polling)
|
|
107
76
|
start() {
|
|
108
77
|
log.info(POLLING_START);
|
|
109
78
|
|
|
110
79
|
splitsSyncTask.start();
|
|
111
|
-
startMySegmentsSyncTasks();
|
|
80
|
+
if (storage.splits.usesSegments()) startMySegmentsSyncTasks();
|
|
112
81
|
},
|
|
113
82
|
|
|
114
83
|
// Stop periodic fetching (polling)
|
|
@@ -125,9 +94,8 @@ export function pollingManagerCSFactory(
|
|
|
125
94
|
// fetch splits and segments
|
|
126
95
|
syncAll() {
|
|
127
96
|
const promises = [splitsSyncTask.execute()];
|
|
128
|
-
forOwn(mySegmentsSyncTasks,
|
|
129
|
-
promises.push(
|
|
130
|
-
mlsSyncTask && promises.push(mlsSyncTask.execute());
|
|
97
|
+
forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
|
|
98
|
+
promises.push(mySegmentsSyncTask.execute());
|
|
131
99
|
});
|
|
132
100
|
return Promise.all(promises);
|
|
133
101
|
},
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IStorageSync } from '../../../storages/types';
|
|
2
|
+
import { IReadinessManager } from '../../../readiness/types';
|
|
2
3
|
import { syncTaskFactory } from '../../syncTask';
|
|
3
4
|
import { IMySegmentsSyncTask } from '../types';
|
|
4
|
-
import {
|
|
5
|
+
import { IFetchMemberships } from '../../../services/types';
|
|
5
6
|
import { mySegmentsFetcherFactory } from '../fetchers/mySegmentsFetcher';
|
|
6
7
|
import { ISettings } from '../../../types';
|
|
7
8
|
import { mySegmentsUpdaterFactory } from '../updaters/mySegmentsUpdater';
|
|
@@ -10,26 +11,24 @@ import { mySegmentsUpdaterFactory } from '../updaters/mySegmentsUpdater';
|
|
|
10
11
|
* Creates a sync task that periodically executes a `mySegmentsUpdater` task
|
|
11
12
|
*/
|
|
12
13
|
export function mySegmentsSyncTaskFactory(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
fetchMemberships: IFetchMemberships,
|
|
15
|
+
storage: IStorageSync,
|
|
16
|
+
readiness: IReadinessManager,
|
|
16
17
|
settings: ISettings,
|
|
17
|
-
matchingKey: string
|
|
18
|
-
segmentsRefreshRate: number,
|
|
19
|
-
NAME: string
|
|
18
|
+
matchingKey: string
|
|
20
19
|
): IMySegmentsSyncTask {
|
|
21
20
|
return syncTaskFactory(
|
|
22
21
|
settings.log,
|
|
23
22
|
mySegmentsUpdaterFactory(
|
|
24
23
|
settings.log,
|
|
25
|
-
mySegmentsFetcherFactory(
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
mySegmentsFetcherFactory(fetchMemberships),
|
|
25
|
+
storage,
|
|
26
|
+
readiness.segments,
|
|
28
27
|
settings.startup.requestTimeoutBeforeReady,
|
|
29
28
|
settings.startup.retriesOnFailureBeforeReady,
|
|
30
29
|
matchingKey
|
|
31
30
|
),
|
|
32
|
-
segmentsRefreshRate,
|
|
33
|
-
|
|
31
|
+
settings.scheduler.segmentsRefreshRate,
|
|
32
|
+
'mySegmentsUpdater',
|
|
34
33
|
);
|
|
35
34
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ISplit } from '../../dtos/types';
|
|
1
|
+
import { IMembershipsResponse, ISplit } from '../../dtos/types';
|
|
2
2
|
import { IReadinessManager } from '../../readiness/types';
|
|
3
3
|
import { IStorageSync } from '../../storages/types';
|
|
4
4
|
import { ITask, ISyncTask } from '../types';
|
|
@@ -7,12 +7,14 @@ export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: nu
|
|
|
7
7
|
|
|
8
8
|
export interface ISegmentsSyncTask extends ISyncTask<[fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number], boolean> { }
|
|
9
9
|
|
|
10
|
-
export type MySegmentsData =
|
|
10
|
+
export type MySegmentsData = IMembershipsResponse | {
|
|
11
|
+
/* segment type */
|
|
12
|
+
isLS?: boolean
|
|
11
13
|
/* segment name */
|
|
12
|
-
name: string
|
|
14
|
+
name: string
|
|
13
15
|
/* action: `true` for add, and `false` for delete */
|
|
14
16
|
add: boolean
|
|
15
|
-
}
|
|
17
|
+
}[]
|
|
16
18
|
|
|
17
19
|
export interface IMySegmentsSyncTask extends ISyncTask<[segmentsData?: MySegmentsData, noCache?: boolean], boolean> { }
|
|
18
20
|
|
|
@@ -20,14 +22,13 @@ export interface IPollingManager extends ITask {
|
|
|
20
22
|
syncAll(): Promise<any>
|
|
21
23
|
splitsSyncTask: ISplitsSyncTask
|
|
22
24
|
segmentsSyncTask: ISyncTask
|
|
23
|
-
largeSegmentsSyncTask?: ISyncTask
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* PollingManager for client-side with support for multiple clients
|
|
28
29
|
*/
|
|
29
30
|
export interface IPollingManagerCS extends IPollingManager {
|
|
30
|
-
add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync):
|
|
31
|
+
add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync): IMySegmentsSyncTask
|
|
31
32
|
remove(matchingKey: string): void;
|
|
32
|
-
get(matchingKey: string):
|
|
33
|
+
get(matchingKey: string): IMySegmentsSyncTask | undefined
|
|
33
34
|
}
|