@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.
Files changed (138) hide show
  1. package/CHANGES.txt +4 -0
  2. package/cjs/logger/constants.js +5 -4
  3. package/cjs/logger/messages/info.js +2 -1
  4. package/cjs/logger/messages/warn.js +1 -1
  5. package/cjs/readiness/readinessManager.js +7 -12
  6. package/cjs/services/splitApi.js +4 -8
  7. package/cjs/storages/AbstractSegmentsCacheSync.js +1 -6
  8. package/cjs/storages/AbstractSplitsCacheAsync.js +2 -2
  9. package/cjs/storages/AbstractSplitsCacheSync.js +7 -5
  10. package/cjs/storages/KeyBuilder.js +0 -3
  11. package/cjs/storages/KeyBuilderCS.js +6 -0
  12. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +23 -2
  13. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +4 -16
  14. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +5 -1
  15. package/cjs/storages/inMemory/SplitsCacheInMemory.js +6 -15
  16. package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +4 -11
  17. package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  18. package/cjs/sync/polling/pollingManagerCS.js +33 -51
  19. package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  20. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +23 -19
  21. package/cjs/sync/streaming/SSEHandler/index.js +7 -8
  22. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +8 -4
  23. package/cjs/sync/streaming/constants.js +2 -3
  24. package/cjs/sync/streaming/parseUtils.js +14 -9
  25. package/cjs/sync/streaming/pushManager.js +29 -52
  26. package/cjs/sync/submitters/telemetrySubmitter.js +0 -2
  27. package/cjs/sync/syncManagerOnline.js +14 -24
  28. package/cjs/utils/constants/index.js +1 -1
  29. package/cjs/utils/settingsValidation/index.js +1 -5
  30. package/esm/logger/constants.js +2 -1
  31. package/esm/logger/messages/info.js +2 -1
  32. package/esm/logger/messages/warn.js +1 -1
  33. package/esm/readiness/readinessManager.js +7 -12
  34. package/esm/services/splitApi.js +5 -9
  35. package/esm/storages/AbstractSegmentsCacheSync.js +1 -6
  36. package/esm/storages/AbstractSplitsCacheAsync.js +2 -2
  37. package/esm/storages/AbstractSplitsCacheSync.js +5 -3
  38. package/esm/storages/KeyBuilder.js +0 -3
  39. package/esm/storages/KeyBuilderCS.js +6 -0
  40. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +23 -2
  41. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -17
  42. package/esm/storages/inMemory/MySegmentsCacheInMemory.js +5 -1
  43. package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -16
  44. package/esm/sync/polling/fetchers/mySegmentsFetcher.js +4 -11
  45. package/esm/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  46. package/esm/sync/polling/pollingManagerCS.js +34 -52
  47. package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  48. package/esm/sync/polling/updaters/mySegmentsUpdater.js +21 -17
  49. package/esm/sync/streaming/SSEHandler/index.js +8 -9
  50. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +8 -4
  51. package/esm/sync/streaming/constants.js +1 -2
  52. package/esm/sync/streaming/parseUtils.js +12 -8
  53. package/esm/sync/streaming/pushManager.js +31 -53
  54. package/esm/sync/submitters/telemetrySubmitter.js +0 -2
  55. package/esm/sync/syncManagerOnline.js +15 -25
  56. package/esm/utils/constants/index.js +1 -1
  57. package/esm/utils/settingsValidation/index.js +1 -5
  58. package/package.json +1 -1
  59. package/src/dtos/types.ts +14 -12
  60. package/src/logger/constants.ts +2 -1
  61. package/src/logger/messages/info.ts +2 -1
  62. package/src/logger/messages/warn.ts +1 -1
  63. package/src/readiness/readinessManager.ts +7 -9
  64. package/src/readiness/types.ts +0 -1
  65. package/src/services/splitApi.ts +6 -11
  66. package/src/services/splitHttpClient.ts +1 -1
  67. package/src/services/types.ts +2 -3
  68. package/src/storages/AbstractSegmentsCacheSync.ts +2 -6
  69. package/src/storages/AbstractSplitsCacheAsync.ts +2 -2
  70. package/src/storages/AbstractSplitsCacheSync.ts +6 -4
  71. package/src/storages/KeyBuilder.ts +0 -3
  72. package/src/storages/KeyBuilderCS.ts +9 -0
  73. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +26 -2
  74. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +5 -20
  75. package/src/storages/inMemory/MySegmentsCacheInMemory.ts +7 -1
  76. package/src/storages/inMemory/SplitsCacheInMemory.ts +7 -13
  77. package/src/storages/types.ts +6 -5
  78. package/src/sync/polling/fetchers/mySegmentsFetcher.ts +7 -14
  79. package/src/sync/polling/fetchers/segmentChangesFetcher.ts +1 -1
  80. package/src/sync/polling/fetchers/types.ts +2 -2
  81. package/src/sync/polling/pollingManagerCS.ts +29 -61
  82. package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +12 -13
  83. package/src/sync/polling/types.ts +8 -7
  84. package/src/sync/polling/updaters/mySegmentsUpdater.ts +20 -16
  85. package/src/sync/streaming/SSEClient/index.ts +4 -6
  86. package/src/sync/streaming/SSEHandler/index.ts +11 -13
  87. package/src/sync/streaming/SSEHandler/types.ts +13 -25
  88. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +11 -7
  89. package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +1 -1
  90. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +1 -1
  91. package/src/sync/streaming/UpdateWorkers/types.ts +2 -2
  92. package/src/sync/streaming/constants.ts +1 -2
  93. package/src/sync/streaming/parseUtils.ts +19 -11
  94. package/src/sync/streaming/pushManager.ts +37 -64
  95. package/src/sync/streaming/types.ts +9 -11
  96. package/src/sync/submitters/telemetrySubmitter.ts +0 -2
  97. package/src/sync/submitters/types.ts +1 -3
  98. package/src/sync/syncManagerOnline.ts +11 -19
  99. package/src/types.ts +1 -26
  100. package/src/utils/constants/index.ts +1 -1
  101. package/src/utils/settingsValidation/index.ts +1 -5
  102. package/types/dtos/types.d.ts +14 -11
  103. package/types/logger/constants.d.ts +2 -1
  104. package/types/readiness/types.d.ts +0 -1
  105. package/types/services/decorateHeaders.d.ts +2 -0
  106. package/types/services/splitApi.d.ts +1 -1
  107. package/types/services/splitHttpClient.d.ts +1 -1
  108. package/types/services/types.d.ts +2 -3
  109. package/types/storages/AbstractSegmentsCacheSync.d.ts +2 -6
  110. package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
  111. package/types/storages/AbstractSplitsCacheSync.d.ts +3 -3
  112. package/types/storages/KeyBuilder.d.ts +0 -1
  113. package/types/storages/KeyBuilderCS.d.ts +2 -0
  114. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +3 -2
  115. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +1 -1
  116. package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +3 -1
  117. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +1 -2
  118. package/types/storages/types.d.ts +4 -4
  119. package/types/sync/polling/fetchers/mySegmentsFetcher.d.ts +2 -2
  120. package/types/sync/polling/fetchers/types.d.ts +2 -2
  121. package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +4 -3
  122. package/types/sync/polling/types.d.ts +6 -12
  123. package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +3 -2
  124. package/types/sync/streaming/SSEHandler/types.d.ts +13 -22
  125. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +2 -2
  126. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
  127. package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
  128. package/types/sync/streaming/UpdateWorkers/types.d.ts +2 -2
  129. package/types/sync/streaming/constants.d.ts +1 -2
  130. package/types/sync/streaming/parseUtils.d.ts +4 -5
  131. package/types/sync/streaming/pushManager.d.ts +0 -2
  132. package/types/sync/streaming/pushManagerCS_Spec1_3.d.ts +9 -0
  133. package/types/sync/streaming/pushManager_Spec1_3.d.ts +9 -0
  134. package/types/sync/streaming/types.d.ts +8 -9
  135. package/types/sync/submitters/types.d.ts +1 -3
  136. package/types/types.d.ts +0 -25
  137. package/types/utils/constants/index.d.ts +1 -1
  138. 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 usesMatcher(matcherType: string): boolean
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::usesMatcher` method
89
+ * This util is intended to simplify the implementation of `splitsCache::usesSegments` method
89
90
  */
90
- export function usesMatcher(split: ISplit, matcherType: string) {
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
- if (matchers[j].matcherType === matcherType) return true;
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[]} segmentNames list of segment names
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, usesMatcher } from '../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 (usesMatcher(split, IN_SEGMENT)) {
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 (usesMatcher(split, IN_SEGMENT)) {
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
- usesMatcher(matcherType: string) {
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(matcherType === IN_SEGMENT ?
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, usesMatcher } from '../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
- // Substract from segments count for the previous version of this Split.
45
- if (usesMatcher(previousSplit, IN_SEGMENT)) this.segmentsCount--;
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 (usesMatcher(split, IN_SEGMENT)) this.segmentsCount++;
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 (usesMatcher(split, IN_SEGMENT)) this.segmentsCount--;
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
- usesMatcher(matcherType: string): boolean {
110
- return this.getChangeNumber() === -1 || (matcherType === IN_SEGMENT ? this.segmentsCount > 0 : this.largeSegmentsCount > 0);
103
+ usesSegments(): boolean {
104
+ return this.getChangeNumber() === -1 || this.segmentsCount > 0;
111
105
  }
112
106
 
113
107
  getNamesByFlagSets(flagSets: string[]): ISet<string>[] {
@@ -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 the given matcher
208
- usesMatcher(matcherType: string): MaybeThenable<boolean>,
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
- usesMatcher(matcherType: string): boolean,
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
- usesMatcher(matcherType: string): Promise<boolean>,
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 { IFetchMySegments, IResponse } from '../../../services/types';
2
- import { IMySegmentsResponse, IMyLargeSegmentsResponse } from '../../../dtos/types';
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(fetchMySegments: IFetchMySegments): IMySegmentsFetcher {
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 `fetchMySegments` promise, such as timeout or time tracker
14
+ // Optional decorator for `fetchMemberships` promise, such as timeout or time tracker
15
15
  decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
16
- ): Promise<string[]> {
16
+ ): Promise<IMembershipsResponse> {
17
17
 
18
- let mySegmentsPromise = fetchMySegments(userMatchingKey, noCache);
18
+ let mySegmentsPromise = fetchMemberships(userMatchingKey, noCache);
19
19
  if (decorator) mySegmentsPromise = decorator(mySegmentsPromise);
20
20
 
21
- // Extract segment names
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 `fetchMySegments` promise, such as timeout or time tracker
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<string[]>
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 for segments and large segments.
27
- const mySegmentsSyncTasks: Record<string, { msSyncTask: IMySegmentsSyncTask, mlsSyncTask?: IMySegmentsSyncTask }> = {};
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 { msSyncTask, mlsSyncTask } = add(matchingKey, readiness, storage);
29
+ const mySegmentsSyncTask = add(matchingKey, readiness, storage);
31
30
 
32
31
  function startMySegmentsSyncTasks() {
33
- const splitsHaveSegments = storage.splits.usesMatcher(IN_SEGMENT);
34
- const splitsHaveLargeSegments = storage.splits.usesMatcher(IN_LARGE_SEGMENT);
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, ({ msSyncTask, mlsSyncTask }) => {
49
- msSyncTask.stop();
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()) startMySegmentsSyncTasks();
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 msSyncTask = mySegmentsSyncTaskFactory(
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
- if (storage.splits.usesMatcher(IN_SEGMENT) && storage.splits.usesMatcher(IN_LARGE_SEGMENT)) readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady);
91
- else setTimeout(smartReady, 0);
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: msSyncTask,
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, function ({ msSyncTask, mlsSyncTask }) {
129
- promises.push(msSyncTask.execute());
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 { ISegmentsCacheSync } from '../../../storages/types';
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 { IFetchMySegments } from '../../../services/types';
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
- fetchMySegments: IFetchMySegments,
14
- mySegmentsCache: ISegmentsCacheSync,
15
- notifyUpdate: () => void,
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(fetchMySegments),
26
- mySegmentsCache,
27
- notifyUpdate,
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
- NAME,
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 = string[] | {
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): { msSyncTask: IMySegmentsSyncTask, mlsSyncTask?: IMySegmentsSyncTask }
31
+ add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync): IMySegmentsSyncTask
31
32
  remove(matchingKey: string): void;
32
- get(matchingKey: string): { msSyncTask: IMySegmentsSyncTask, mlsSyncTask?: IMySegmentsSyncTask } | undefined
33
+ get(matchingKey: string): IMySegmentsSyncTask | undefined
33
34
  }