@splitsoftware/splitio-commons 1.16.1-rc.0 → 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 (142) 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 +3 -9
  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/storages/pluggable/inMemoryWrapper.js +1 -1
  17. package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +4 -7
  18. package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  19. package/cjs/sync/polling/pollingManagerCS.js +30 -54
  20. package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  21. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +25 -27
  22. package/cjs/sync/streaming/SSEHandler/index.js +7 -8
  23. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +18 -5
  24. package/cjs/sync/streaming/constants.js +2 -3
  25. package/cjs/sync/streaming/parseUtils.js +14 -9
  26. package/cjs/sync/streaming/pushManager.js +29 -53
  27. package/cjs/sync/submitters/telemetrySubmitter.js +0 -2
  28. package/cjs/sync/syncManagerOnline.js +14 -24
  29. package/cjs/utils/constants/index.js +1 -1
  30. package/cjs/utils/settingsValidation/index.js +1 -5
  31. package/esm/logger/constants.js +2 -1
  32. package/esm/logger/messages/info.js +2 -1
  33. package/esm/logger/messages/warn.js +1 -1
  34. package/esm/readiness/readinessManager.js +3 -9
  35. package/esm/services/splitApi.js +5 -9
  36. package/esm/storages/AbstractSegmentsCacheSync.js +1 -6
  37. package/esm/storages/AbstractSplitsCacheAsync.js +2 -2
  38. package/esm/storages/AbstractSplitsCacheSync.js +5 -3
  39. package/esm/storages/KeyBuilder.js +0 -3
  40. package/esm/storages/KeyBuilderCS.js +6 -0
  41. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +23 -2
  42. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -17
  43. package/esm/storages/inMemory/MySegmentsCacheInMemory.js +5 -1
  44. package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -16
  45. package/esm/storages/pluggable/inMemoryWrapper.js +1 -1
  46. package/esm/sync/polling/fetchers/mySegmentsFetcher.js +4 -7
  47. package/esm/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  48. package/esm/sync/polling/pollingManagerCS.js +31 -55
  49. package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  50. package/esm/sync/polling/updaters/mySegmentsUpdater.js +23 -25
  51. package/esm/sync/streaming/SSEHandler/index.js +8 -9
  52. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +18 -5
  53. package/esm/sync/streaming/constants.js +1 -2
  54. package/esm/sync/streaming/parseUtils.js +12 -8
  55. package/esm/sync/streaming/pushManager.js +31 -54
  56. package/esm/sync/submitters/telemetrySubmitter.js +0 -2
  57. package/esm/sync/syncManagerOnline.js +15 -25
  58. package/esm/utils/constants/index.js +1 -1
  59. package/esm/utils/settingsValidation/index.js +1 -5
  60. package/package.json +1 -1
  61. package/src/dtos/types.ts +14 -8
  62. package/src/logger/constants.ts +2 -1
  63. package/src/logger/messages/info.ts +2 -1
  64. package/src/logger/messages/warn.ts +1 -1
  65. package/src/readiness/readinessManager.ts +3 -7
  66. package/src/readiness/types.ts +0 -1
  67. package/src/services/splitApi.ts +6 -11
  68. package/src/services/splitHttpClient.ts +1 -1
  69. package/src/services/types.ts +2 -3
  70. package/src/storages/AbstractSegmentsCacheSync.ts +2 -6
  71. package/src/storages/AbstractSplitsCacheAsync.ts +2 -2
  72. package/src/storages/AbstractSplitsCacheSync.ts +6 -4
  73. package/src/storages/KeyBuilder.ts +0 -3
  74. package/src/storages/KeyBuilderCS.ts +9 -0
  75. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +26 -2
  76. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +5 -20
  77. package/src/storages/inMemory/MySegmentsCacheInMemory.ts +7 -1
  78. package/src/storages/inMemory/SplitsCacheInMemory.ts +7 -13
  79. package/src/storages/pluggable/inMemoryWrapper.ts +1 -1
  80. package/src/storages/types.ts +6 -5
  81. package/src/sync/polling/fetchers/mySegmentsFetcher.ts +7 -10
  82. package/src/sync/polling/fetchers/segmentChangesFetcher.ts +1 -1
  83. package/src/sync/polling/fetchers/types.ts +2 -2
  84. package/src/sync/polling/pollingManagerCS.ts +27 -62
  85. package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +11 -11
  86. package/src/sync/polling/types.ts +9 -8
  87. package/src/sync/polling/updaters/mySegmentsUpdater.ts +22 -24
  88. package/src/sync/streaming/SSEClient/index.ts +4 -6
  89. package/src/sync/streaming/SSEHandler/index.ts +11 -13
  90. package/src/sync/streaming/SSEHandler/types.ts +13 -25
  91. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +21 -7
  92. package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +1 -1
  93. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +1 -1
  94. package/src/sync/streaming/UpdateWorkers/types.ts +2 -2
  95. package/src/sync/streaming/constants.ts +1 -2
  96. package/src/sync/streaming/parseUtils.ts +19 -11
  97. package/src/sync/streaming/pushManager.ts +37 -65
  98. package/src/sync/streaming/types.ts +9 -11
  99. package/src/sync/submitters/telemetrySubmitter.ts +0 -2
  100. package/src/sync/submitters/types.ts +1 -3
  101. package/src/sync/syncManagerOnline.ts +11 -19
  102. package/src/types.ts +1 -26
  103. package/src/utils/constants/index.ts +1 -1
  104. package/src/utils/settingsValidation/index.ts +1 -5
  105. package/types/dtos/types.d.ts +14 -7
  106. package/types/logger/constants.d.ts +2 -1
  107. package/types/readiness/types.d.ts +0 -1
  108. package/types/services/decorateHeaders.d.ts +2 -0
  109. package/types/services/splitApi.d.ts +1 -1
  110. package/types/services/splitHttpClient.d.ts +1 -1
  111. package/types/services/types.d.ts +2 -3
  112. package/types/storages/AbstractSegmentsCacheSync.d.ts +2 -6
  113. package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
  114. package/types/storages/AbstractSplitsCacheSync.d.ts +3 -3
  115. package/types/storages/KeyBuilder.d.ts +0 -1
  116. package/types/storages/KeyBuilderCS.d.ts +2 -0
  117. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +3 -2
  118. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +1 -1
  119. package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +3 -1
  120. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +1 -2
  121. package/types/storages/pluggable/inMemoryWrapper.d.ts +1 -1
  122. package/types/storages/types.d.ts +4 -4
  123. package/types/sync/polling/fetchers/mySegmentsFetcher.d.ts +2 -2
  124. package/types/sync/polling/fetchers/types.d.ts +2 -2
  125. package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +4 -3
  126. package/types/sync/polling/types.d.ts +7 -13
  127. package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +3 -2
  128. package/types/sync/streaming/SSEHandler/types.d.ts +13 -22
  129. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +2 -2
  130. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
  131. package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
  132. package/types/sync/streaming/UpdateWorkers/types.d.ts +2 -2
  133. package/types/sync/streaming/constants.d.ts +1 -2
  134. package/types/sync/streaming/parseUtils.d.ts +4 -5
  135. package/types/sync/streaming/pushManager.d.ts +0 -2
  136. package/types/sync/streaming/pushManagerCS_Spec1_3.d.ts +9 -0
  137. package/types/sync/streaming/pushManager_Spec1_3.d.ts +9 -0
  138. package/types/sync/streaming/types.d.ts +8 -9
  139. package/types/sync/submitters/types.d.ts +1 -3
  140. package/types/types.d.ts +0 -25
  141. package/types/utils/constants/index.d.ts +1 -1
  142. package/types/utils/settingsValidation/index.d.ts +0 -2
@@ -12,7 +12,7 @@ const messageNoFetch = 'Global fetch API is not available.';
12
12
  * @param settings SDK settings, used to access authorizationKey, logger instance and metadata (SDK version, ip and hostname) to set additional headers
13
13
  * @param platform object containing environment-specific dependencies
14
14
  */
15
- export function splitHttpClientFactory(settings: ISettings, { getOptions, getFetch }: IPlatform): ISplitHttpClient {
15
+ export function splitHttpClientFactory(settings: ISettings, { getOptions, getFetch }: Pick<IPlatform, 'getOptions' | 'getFetch'>): ISplitHttpClient {
16
16
 
17
17
  const { log, core: { authorizationKey }, version, runtime: { ip, hostname } } = settings;
18
18
  const options = getOptions && getOptions(settings);
@@ -39,7 +39,7 @@ export type IFetchSplitChanges = (since: number, noCache?: boolean, till?: numbe
39
39
 
40
40
  export type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean, till?: number) => Promise<IResponse>
41
41
 
42
- export type IFetchMySegments = (userMatchingKey: string, noCache?: boolean) => Promise<IResponse>
42
+ export type IFetchMemberships = (userMatchingKey: string, noCache?: boolean) => Promise<IResponse>
43
43
 
44
44
  export type IPostEventsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>
45
45
 
@@ -61,8 +61,7 @@ export interface ISplitApi {
61
61
  fetchAuth: IFetchAuth
62
62
  fetchSplitChanges: IFetchSplitChanges
63
63
  fetchSegmentChanges: IFetchSegmentChanges
64
- fetchMySegments: IFetchMySegments
65
- fetchMyLargeSegments: IFetchMySegments
64
+ fetchMemberships: IFetchMemberships
66
65
  postEventsBulk: IPostEventsBulk
67
66
  postUniqueKeysBulkCs: IPostUniqueKeysBulkCs
68
67
  postUniqueKeysBulkSs: IPostUniqueKeysBulkSs
@@ -54,15 +54,11 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
54
54
  */
55
55
  setChangeNumber(name: string, changeNumber: number): boolean { return true; }
56
56
 
57
- /**
58
- * For server-side synchronizer: get the change number of `name` segment.
59
- * For client-side synchronizer: the method is not used.
60
- */
61
- getChangeNumber(name: string): number { return -1; }
57
+ abstract getChangeNumber(name: string): number
62
58
 
63
59
  /**
64
60
  * For server-side synchronizer: the method is not used.
65
61
  * For client-side synchronizer: reset the cache with the given list of segments.
66
62
  */
67
- resetSegments(names: string[]): boolean { return true; }
63
+ resetSegments(names: string[], changeNumber?: number): boolean { return true; }
68
64
  }
@@ -22,9 +22,9 @@ export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
22
22
  abstract trafficTypeExists(trafficType: string): Promise<boolean>
23
23
  abstract clear(): Promise<boolean | void>
24
24
 
25
- // @TODO revisit segment-related methods ('usesMatcher', 'getRegisteredSegments', 'registerSegments')
25
+ // @TODO revisit segment-related methods ('usesSegments', 'getRegisteredSegments', 'registerSegments')
26
26
  // noop, just keeping the interface. This is used by standalone client-side API only, and so only implemented by InMemory and InLocalStorage.
27
- usesMatcher(): Promise<boolean> {
27
+ usesSegments(): Promise<boolean> {
28
28
  return Promise.resolve(true);
29
29
  }
30
30
 
@@ -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>[] {
@@ -7,7 +7,7 @@ import { ISet, setToArray, _Set } from '../../utils/lang/sets';
7
7
  * The `_cache` property is the object were items are stored.
8
8
  * Intended for testing purposes.
9
9
  *
10
- * @param connDelay delay in millis for `connect` resolve. If not provided, `connect` resolves inmediatelly.
10
+ * @param connDelay delay in millis for `connect` resolve. If not provided, `connect` resolves immediately.
11
11
  */
12
12
  export function inMemoryWrapperFactory(connDelay?: number): IPluggableStorageWrapper & { _cache: Record<string, string | string[] | ISet<string>>, _setConnDelay(connDelay: number): void } {
13
13
 
@@ -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
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,27 +1,24 @@
1
- import { IFetchMySegments, IResponse } from '../../../services/types';
2
- import { IMySegmentsResponseItem } 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 => json.mySegments.map((segment: IMySegmentsResponseItem) => segment.name));
21
+ return mySegmentsPromise.then(resp => resp.json());
25
22
  };
26
23
 
27
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,95 +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();
39
-
40
- if (mlsSyncTask) {
41
- if (splitsHaveLargeSegments) mlsSyncTask.start();
42
- else mlsSyncTask.stop();
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
 
54
43
  // smart pausing
55
44
  readiness.splits.on(SDK_SPLITS_ARRIVED, () => {
56
- // smart pausing of mySegments polling
57
- 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
+ }
58
55
  });
59
56
 
60
57
  function add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync) {
61
- const msSyncTask = mySegmentsSyncTaskFactory(
62
- splitApi.fetchMySegments,
63
- storage.segments,
64
- () => {
65
- if (storage.splits.usesMatcher(IN_SEGMENT)) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
66
- },
67
- settings,
68
- matchingKey,
69
- settings.scheduler.segmentsRefreshRate
70
- );
71
-
72
- let mlsSyncTask;
73
- if (settings.sync.largeSegmentsEnabled) {
74
- mlsSyncTask = mySegmentsSyncTaskFactory(
75
- splitApi.fetchMyLargeSegments,
76
- storage.largeSegments!,
77
- () => {
78
- if (readiness.largeSegments && storage.splits.usesMatcher(IN_LARGE_SEGMENT)) readiness.largeSegments.emit(SDK_SEGMENTS_ARRIVED);
79
- },
80
- settings,
81
- matchingKey,
82
- settings.scheduler.largeSegmentsRefreshRate
83
- );
84
- }
58
+ const mySegmentsSyncTask = mySegmentsSyncTaskFactory(splitApi.fetchMemberships, storage, readiness, settings, matchingKey);
85
59
 
86
60
  // smart ready
87
61
  function smartReady() {
88
- if (!readiness.isReady()) {
89
- if (!storage.splits.usesMatcher(IN_SEGMENT)) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
90
- if (readiness.largeSegments && !storage.splits.usesMatcher(IN_LARGE_SEGMENT)) readiness.largeSegments.emit(SDK_SEGMENTS_ARRIVED);
91
- }
62
+ if (!readiness.isReady() && !storage.splits.usesSegments()) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
92
63
  }
93
- if (!storage.splits.usesMatcher(IN_SEGMENT) && !storage.splits.usesMatcher(IN_LARGE_SEGMENT)) setTimeout(smartReady, 0);
64
+ if (!storage.splits.usesSegments()) setTimeout(smartReady, 0);
94
65
  else readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady);
95
66
 
96
- mySegmentsSyncTasks[matchingKey] = { msSyncTask: msSyncTask, mlsSyncTask: mlsSyncTask };
97
-
98
- return {
99
- msSyncTask,
100
- mlsSyncTask
101
- };
67
+ mySegmentsSyncTasks[matchingKey] = mySegmentsSyncTask;
68
+ return mySegmentsSyncTask;
102
69
  }
103
70
 
104
71
  return {
105
72
  splitsSyncTask,
106
- segmentsSyncTask: msSyncTask,
107
- largeSegmentsSyncTask: mlsSyncTask,
73
+ segmentsSyncTask: mySegmentsSyncTask,
108
74
 
109
75
  // Start periodic fetching (polling)
110
76
  start() {
111
77
  log.info(POLLING_START);
112
78
 
113
79
  splitsSyncTask.start();
114
- startMySegmentsSyncTasks();
80
+ if (storage.splits.usesSegments()) startMySegmentsSyncTasks();
115
81
  },
116
82
 
117
83
  // Stop periodic fetching (polling)
@@ -128,9 +94,8 @@ export function pollingManagerCSFactory(
128
94
  // fetch splits and segments
129
95
  syncAll() {
130
96
  const promises = [splitsSyncTask.execute()];
131
- forOwn(mySegmentsSyncTasks, function ({ msSyncTask, mlsSyncTask }) {
132
- promises.push(msSyncTask.execute());
133
- mlsSyncTask && promises.push(mlsSyncTask.execute());
97
+ forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
98
+ promises.push(mySegmentsSyncTask.execute());
134
99
  });
135
100
  return Promise.all(promises);
136
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,25 +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
18
+ matchingKey: string
19
19
  ): IMySegmentsSyncTask {
20
20
  return syncTaskFactory(
21
21
  settings.log,
22
22
  mySegmentsUpdaterFactory(
23
23
  settings.log,
24
- mySegmentsFetcherFactory(fetchMySegments),
25
- mySegmentsCache,
26
- notifyUpdate,
24
+ mySegmentsFetcherFactory(fetchMemberships),
25
+ storage,
26
+ readiness.segments,
27
27
  settings.startup.requestTimeoutBeforeReady,
28
28
  settings.startup.retriesOnFailureBeforeReady,
29
29
  matchingKey
30
30
  ),
31
- segmentsRefreshRate,
31
+ settings.scheduler.segmentsRefreshRate,
32
32
  'mySegmentsUpdater',
33
33
  );
34
34
  }