@splitsoftware/splitio-commons 2.2.1-rc.4 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/CHANGES.txt +26 -26
  2. package/README.md +0 -1
  3. package/cjs/evaluator/combiners/and.js +6 -2
  4. package/cjs/evaluator/combiners/ifelseif.js +6 -6
  5. package/cjs/evaluator/condition/index.js +5 -6
  6. package/cjs/evaluator/index.js +7 -7
  7. package/cjs/evaluator/matchers/index.js +1 -3
  8. package/cjs/evaluator/matchers/matcherTypes.js +0 -1
  9. package/cjs/evaluator/matchersTransform/index.js +0 -4
  10. package/cjs/evaluator/parser/index.js +2 -2
  11. package/cjs/evaluator/value/sanitize.js +0 -1
  12. package/cjs/logger/constants.js +3 -4
  13. package/cjs/logger/messages/debug.js +2 -3
  14. package/cjs/logger/messages/warn.js +1 -1
  15. package/cjs/services/splitApi.js +4 -3
  16. package/cjs/services/splitHttpClient.js +4 -1
  17. package/cjs/storages/AbstractSplitsCacheSync.js +2 -5
  18. package/cjs/storages/KeyBuilder.js +0 -9
  19. package/cjs/storages/KeyBuilderCS.js +0 -3
  20. package/cjs/storages/KeyBuilderSS.js +0 -3
  21. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +14 -9
  22. package/cjs/storages/inLocalStorage/index.js +1 -5
  23. package/cjs/storages/inLocalStorage/validateCache.js +1 -2
  24. package/cjs/storages/inMemory/InMemoryStorage.js +0 -3
  25. package/cjs/storages/inMemory/InMemoryStorageCS.js +0 -4
  26. package/cjs/storages/inMemory/SplitsCacheInMemory.js +0 -1
  27. package/cjs/storages/inRedis/index.js +0 -2
  28. package/cjs/storages/pluggable/index.js +0 -2
  29. package/cjs/sync/polling/fetchers/splitChangesFetcher.js +4 -54
  30. package/cjs/sync/polling/pollingManagerCS.js +7 -7
  31. package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
  32. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  33. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  34. package/cjs/sync/polling/updaters/splitChangesUpdater.js +33 -59
  35. package/cjs/sync/streaming/SSEHandler/index.js +0 -1
  36. package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +77 -106
  37. package/cjs/sync/streaming/constants.js +1 -2
  38. package/cjs/sync/streaming/pushManager.js +16 -3
  39. package/cjs/sync/syncManagerOnline.js +2 -2
  40. package/cjs/utils/constants/index.js +2 -6
  41. package/esm/evaluator/combiners/and.js +6 -2
  42. package/esm/evaluator/combiners/ifelseif.js +7 -7
  43. package/esm/evaluator/condition/index.js +5 -6
  44. package/esm/evaluator/index.js +7 -7
  45. package/esm/evaluator/matchers/index.js +1 -3
  46. package/esm/evaluator/matchers/matcherTypes.js +0 -1
  47. package/esm/evaluator/matchersTransform/index.js +0 -4
  48. package/esm/evaluator/parser/index.js +2 -2
  49. package/esm/evaluator/value/sanitize.js +0 -1
  50. package/esm/logger/constants.js +0 -1
  51. package/esm/logger/messages/debug.js +2 -3
  52. package/esm/logger/messages/warn.js +1 -1
  53. package/esm/services/splitApi.js +4 -3
  54. package/esm/services/splitHttpClient.js +4 -1
  55. package/esm/storages/AbstractSplitsCacheSync.js +2 -5
  56. package/esm/storages/KeyBuilder.js +0 -9
  57. package/esm/storages/KeyBuilderCS.js +0 -3
  58. package/esm/storages/KeyBuilderSS.js +0 -3
  59. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +14 -9
  60. package/esm/storages/inLocalStorage/index.js +1 -5
  61. package/esm/storages/inLocalStorage/validateCache.js +1 -2
  62. package/esm/storages/inMemory/InMemoryStorage.js +0 -3
  63. package/esm/storages/inMemory/InMemoryStorageCS.js +0 -4
  64. package/esm/storages/inMemory/SplitsCacheInMemory.js +0 -1
  65. package/esm/storages/inRedis/index.js +0 -2
  66. package/esm/storages/pluggable/index.js +0 -2
  67. package/esm/sync/polling/fetchers/splitChangesFetcher.js +4 -54
  68. package/esm/sync/polling/pollingManagerCS.js +7 -7
  69. package/esm/sync/polling/syncTasks/splitsSyncTask.js +1 -1
  70. package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  71. package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  72. package/esm/sync/polling/updaters/splitChangesUpdater.js +33 -59
  73. package/esm/sync/streaming/SSEHandler/index.js +1 -2
  74. package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +73 -102
  75. package/esm/sync/streaming/constants.js +0 -1
  76. package/esm/sync/streaming/pushManager.js +19 -6
  77. package/esm/sync/syncManagerOnline.js +2 -2
  78. package/esm/utils/constants/index.js +1 -5
  79. package/package.json +1 -1
  80. package/src/dtos/types.ts +8 -37
  81. package/src/evaluator/Engine.ts +1 -1
  82. package/src/evaluator/combiners/and.ts +4 -5
  83. package/src/evaluator/combiners/ifelseif.ts +9 -7
  84. package/src/evaluator/condition/engineUtils.ts +1 -1
  85. package/src/evaluator/condition/index.ts +12 -12
  86. package/src/evaluator/index.ts +7 -7
  87. package/src/evaluator/matchers/index.ts +1 -3
  88. package/src/evaluator/matchers/matcherTypes.ts +0 -1
  89. package/src/evaluator/matchersTransform/index.ts +0 -3
  90. package/src/evaluator/parser/index.ts +3 -3
  91. package/src/evaluator/types.ts +2 -2
  92. package/src/evaluator/value/index.ts +2 -2
  93. package/src/evaluator/value/sanitize.ts +4 -5
  94. package/src/logger/constants.ts +0 -1
  95. package/src/logger/messages/debug.ts +2 -3
  96. package/src/logger/messages/warn.ts +1 -1
  97. package/src/sdkFactory/types.ts +1 -1
  98. package/src/sdkManager/index.ts +1 -1
  99. package/src/services/splitApi.ts +4 -3
  100. package/src/services/splitHttpClient.ts +4 -1
  101. package/src/services/types.ts +1 -1
  102. package/src/storages/AbstractSplitsCacheSync.ts +3 -6
  103. package/src/storages/KeyBuilder.ts +0 -12
  104. package/src/storages/KeyBuilderCS.ts +0 -4
  105. package/src/storages/KeyBuilderSS.ts +0 -4
  106. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +14 -10
  107. package/src/storages/inLocalStorage/index.ts +1 -5
  108. package/src/storages/inLocalStorage/validateCache.ts +1 -3
  109. package/src/storages/inMemory/InMemoryStorage.ts +0 -3
  110. package/src/storages/inMemory/InMemoryStorageCS.ts +0 -4
  111. package/src/storages/inMemory/SplitsCacheInMemory.ts +0 -1
  112. package/src/storages/inRedis/index.ts +0 -2
  113. package/src/storages/pluggable/index.ts +0 -2
  114. package/src/storages/types.ts +1 -33
  115. package/src/sync/polling/fetchers/splitChangesFetcher.ts +4 -65
  116. package/src/sync/polling/fetchers/types.ts +0 -1
  117. package/src/sync/polling/pollingManagerCS.ts +7 -7
  118. package/src/sync/polling/syncTasks/splitsSyncTask.ts +1 -1
  119. package/src/sync/polling/types.ts +2 -2
  120. package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -2
  121. package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
  122. package/src/sync/polling/updaters/splitChangesUpdater.ts +43 -70
  123. package/src/sync/streaming/SSEHandler/index.ts +1 -2
  124. package/src/sync/streaming/SSEHandler/types.ts +2 -2
  125. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +68 -98
  126. package/src/sync/streaming/constants.ts +0 -1
  127. package/src/sync/streaming/parseUtils.ts +2 -2
  128. package/src/sync/streaming/pushManager.ts +18 -6
  129. package/src/sync/streaming/types.ts +2 -3
  130. package/src/sync/syncManagerOnline.ts +2 -2
  131. package/src/utils/constants/index.ts +1 -6
  132. package/src/utils/lang/index.ts +1 -1
  133. package/cjs/evaluator/matchers/rbsegment.js +0 -54
  134. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -117
  135. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +0 -61
  136. package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +0 -64
  137. package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +0 -64
  138. package/esm/evaluator/matchers/rbsegment.js +0 -50
  139. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -114
  140. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +0 -58
  141. package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +0 -61
  142. package/esm/storages/pluggable/RBSegmentsCachePluggable.js +0 -61
  143. package/src/evaluator/matchers/rbsegment.ts +0 -71
  144. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +0 -136
  145. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +0 -68
  146. package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +0 -79
  147. package/src/storages/pluggable/RBSegmentsCachePluggable.ts +0 -76
@@ -1,18 +1,16 @@
1
1
  import { ISegmentsCacheBase, IStorageBase } from '../../../storages/types';
2
2
  import { ISplitChangesFetcher } from '../fetchers/types';
3
- import { IRBSegment, ISplit, ISplitChangesResponse, ISplitFiltersValidation, MaybeThenable } from '../../../dtos/types';
3
+ import { ISplit, ISplitChangesResponse, ISplitFiltersValidation } from '../../../dtos/types';
4
4
  import { ISplitsEventEmitter } from '../../../readiness/types';
5
5
  import { timeout } from '../../../utils/promise/timeout';
6
6
  import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
7
7
  import { ILogger } from '../../../logger/types';
8
- import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_RBS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
8
+ import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
9
9
  import { startsWith } from '../../../utils/lang';
10
- import { IN_RULE_BASED_SEGMENT, IN_SEGMENT, RULE_BASED_SEGMENT, STANDARD_SEGMENT } from '../../../utils/constants';
10
+ import { IN_SEGMENT } from '../../../utils/constants';
11
11
  import { setToArray } from '../../../utils/lang/sets';
12
- import { SPLIT_UPDATE } from '../../streaming/constants';
13
12
 
14
- export type InstantUpdate = { payload: ISplit | IRBSegment, changeNumber: number, type: string };
15
- type SplitChangesUpdater = (noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) => Promise<boolean>
13
+ type ISplitChangesUpdater = (noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }) => Promise<boolean>
16
14
 
17
15
  // Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
18
16
  // Returns a promise that could be rejected.
@@ -26,35 +24,27 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise<boolean> {
26
24
  }
27
25
 
28
26
  /**
29
- * Collect segments from a raw FF or RBS definition.
27
+ * Collect segments from a raw split definition.
30
28
  * Exported for testing purposes.
31
29
  */
32
- export function parseSegments(ruleEntity: ISplit | IRBSegment, matcherType: typeof IN_SEGMENT | typeof IN_RULE_BASED_SEGMENT = IN_SEGMENT): Set<string> {
33
- const { conditions = [], excluded } = ruleEntity as IRBSegment;
34
-
35
- const segments = new Set<string>();
36
- if (excluded && excluded.segments) {
37
- excluded.segments.forEach(({ type, name }) => {
38
- if ((type === STANDARD_SEGMENT && matcherType === IN_SEGMENT) || (type === RULE_BASED_SEGMENT && matcherType === IN_RULE_BASED_SEGMENT)) {
39
- segments.add(name);
40
- }
41
- });
42
- }
30
+ export function parseSegments({ conditions }: ISplit): Set<string> {
31
+ let segments = new Set<string>();
43
32
 
44
33
  for (let i = 0; i < conditions.length; i++) {
45
34
  const matchers = conditions[i].matcherGroup.matchers;
46
35
 
47
36
  matchers.forEach(matcher => {
48
- if (matcher.matcherType === matcherType) segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
37
+ if (matcher.matcherType === IN_SEGMENT) segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
49
38
  });
50
39
  }
51
40
 
52
41
  return segments;
53
42
  }
54
43
 
55
- interface ISplitMutations<T extends ISplit | IRBSegment> {
56
- added: T[],
57
- removed: T[]
44
+ interface ISplitMutations {
45
+ added: ISplit[],
46
+ removed: ISplit[],
47
+ segments: string[]
58
48
  }
59
49
 
60
50
  /**
@@ -83,21 +73,25 @@ function matchFilters(featureFlag: ISplit, filters: ISplitFiltersValidation) {
83
73
  * i.e., an object with added splits, removed splits and used segments.
84
74
  * Exported for testing purposes.
85
75
  */
86
- export function computeMutation<T extends ISplit | IRBSegment>(rules: Array<T>, segments: Set<string>, filters?: ISplitFiltersValidation): ISplitMutations<T> {
87
-
88
- return rules.reduce((accum, ruleEntity) => {
89
- if (ruleEntity.status === 'ACTIVE' && (!filters || matchFilters(ruleEntity as ISplit, filters))) {
90
- accum.added.push(ruleEntity);
76
+ export function computeSplitsMutation(entries: ISplit[], filters: ISplitFiltersValidation): ISplitMutations {
77
+ const segments = new Set<string>();
78
+ const computed = entries.reduce((accum, split) => {
79
+ if (split.status === 'ACTIVE' && matchFilters(split, filters)) {
80
+ accum.added.push(split);
91
81
 
92
- parseSegments(ruleEntity).forEach((segmentName: string) => {
82
+ parseSegments(split).forEach((segmentName: string) => {
93
83
  segments.add(segmentName);
94
84
  });
95
85
  } else {
96
- accum.removed.push(ruleEntity);
86
+ accum.removed.push(split);
97
87
  }
98
88
 
99
89
  return accum;
100
- }, { added: [], removed: [] } as ISplitMutations<T>);
90
+ }, { added: [], removed: [], segments: [] } as ISplitMutations);
91
+
92
+ computed.segments = setToArray(segments);
93
+
94
+ return computed;
101
95
  }
102
96
 
103
97
  /**
@@ -117,14 +111,14 @@ export function computeMutation<T extends ISplit | IRBSegment>(rules: Array<T>,
117
111
  export function splitChangesUpdaterFactory(
118
112
  log: ILogger,
119
113
  splitChangesFetcher: ISplitChangesFetcher,
120
- storage: Pick<IStorageBase, 'splits' | 'rbSegments' | 'segments'>,
114
+ storage: Pick<IStorageBase, 'splits' | 'segments'>,
121
115
  splitFiltersValidation: ISplitFiltersValidation,
122
116
  splitsEventEmitter?: ISplitsEventEmitter,
123
117
  requestTimeoutBeforeReady: number = 0,
124
118
  retriesOnFailureBeforeReady: number = 0,
125
119
  isClientSide?: boolean
126
- ): SplitChangesUpdater {
127
- const { splits, rbSegments, segments } = storage;
120
+ ): ISplitChangesUpdater {
121
+ const { splits, segments } = storage;
128
122
 
129
123
  let startingUp = true;
130
124
 
@@ -141,53 +135,32 @@ export function splitChangesUpdaterFactory(
141
135
  * @param noCache - true to revalidate data to fetch
142
136
  * @param till - query param to bypass CDN requests
143
137
  */
144
- return function splitChangesUpdater(noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) {
138
+ return function splitChangesUpdater(noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }) {
145
139
 
146
140
  /**
147
141
  * @param since - current changeNumber at splitsCache
148
142
  * @param retry - current number of retry attempts
149
143
  */
150
- function _splitChangesUpdater(sinces: [number, number], retry = 0): Promise<boolean> {
151
- const [since, rbSince] = sinces;
152
- log.debug(SYNC_SPLITS_FETCH, sinces);
153
- return Promise.resolve(
154
- instantUpdate ?
155
- instantUpdate.type === SPLIT_UPDATE ?
156
- // IFFU edge case: a change to a flag that adds an IN_RULE_BASED_SEGMENT matcher that is not present yet
157
- Promise.resolve(rbSegments.contains(parseSegments(instantUpdate.payload, IN_RULE_BASED_SEGMENT))).then((contains) => {
158
- return contains ?
159
- { ff: { d: [instantUpdate.payload as ISplit], t: instantUpdate.changeNumber } } :
160
- splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator);
161
- }) :
162
- { rbs: { d: [instantUpdate.payload as IRBSegment], t: instantUpdate.changeNumber } } :
163
- splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator)
144
+ function _splitChangesUpdater(since: number, retry = 0): Promise<boolean> {
145
+ log.debug(SYNC_SPLITS_FETCH, [since]);
146
+ return Promise.resolve(splitUpdateNotification ?
147
+ { splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
148
+ splitChangesFetcher(since, noCache, till, _promiseDecorator)
164
149
  )
165
150
  .then((splitChanges: ISplitChangesResponse) => {
166
151
  startingUp = false;
167
152
 
168
- const usedSegments = new Set<string>();
153
+ const mutation = computeSplitsMutation(splitChanges.splits, splitFiltersValidation);
169
154
 
170
- let ffUpdate: MaybeThenable<boolean> = false;
171
- if (splitChanges.ff) {
172
- const { added, removed } = computeMutation(splitChanges.ff.d, usedSegments, splitFiltersValidation);
173
- log.debug(SYNC_SPLITS_UPDATE, [added.length, removed.length]);
174
- ffUpdate = splits.update(added, removed, splitChanges.ff.t);
175
- }
176
-
177
- let rbsUpdate: MaybeThenable<boolean> = false;
178
- if (splitChanges.rbs) {
179
- const { added, removed } = computeMutation(splitChanges.rbs.d, usedSegments);
180
- log.debug(SYNC_RBS_UPDATE, [added.length, removed.length]);
181
- rbsUpdate = rbSegments.update(added, removed, splitChanges.rbs.t);
182
- }
155
+ log.debug(SYNC_SPLITS_UPDATE, [mutation.added.length, mutation.removed.length, mutation.segments.length]);
183
156
 
184
- return Promise.all([ffUpdate, rbsUpdate,
185
- // @TODO if at least 1 segment fetch fails due to 404 and other segments are updated in the storage, SDK_UPDATE is not emitted
186
- segments.registerSegments(setToArray(usedSegments))
187
- ]).then(([ffChanged, rbsChanged]) => {
157
+ return Promise.all([
158
+ splits.update(mutation.added, mutation.removed, splitChanges.till),
159
+ segments.registerSegments(mutation.segments)
160
+ ]).then(([isThereUpdate]) => {
188
161
  if (splitsEventEmitter) {
189
162
  // To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
190
- return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
163
+ return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && isThereUpdate && (isClientSide || checkAllSegmentsExist(segments))))
191
164
  .catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
192
165
  .then(emitSplitsArrivedEvent => {
193
166
  // emit SDK events
@@ -204,7 +177,7 @@ export function splitChangesUpdaterFactory(
204
177
  if (startingUp && retriesOnFailureBeforeReady > retry) {
205
178
  retry += 1;
206
179
  log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
207
- return _splitChangesUpdater(sinces, retry);
180
+ return _splitChangesUpdater(since, retry);
208
181
  } else {
209
182
  startingUp = false;
210
183
  }
@@ -212,7 +185,7 @@ export function splitChangesUpdaterFactory(
212
185
  });
213
186
  }
214
187
 
215
- // `getChangeNumber` never rejects or throws error
216
- return Promise.all([splits.getChangeNumber(), rbSegments.getChangeNumber()]).then(_splitChangesUpdater);
188
+ let sincePromise = Promise.resolve(splits.getChangeNumber()); // `getChangeNumber` never rejects or throws error
189
+ return sincePromise.then(_splitChangesUpdater);
217
190
  };
218
191
  }
@@ -1,6 +1,6 @@
1
1
  import { errorParser, messageParser } from './NotificationParser';
2
2
  import { notificationKeeperFactory } from './NotificationKeeper';
3
- import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, RB_SEGMENT_UPDATE } from '../constants';
3
+ import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE } from '../constants';
4
4
  import { IPushEventEmitter } from '../types';
5
5
  import { ISseEventHandler } from '../SSEClient/types';
6
6
  import { INotificationError, INotificationMessage } from './types';
@@ -84,7 +84,6 @@ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter,
84
84
  case MEMBERSHIPS_MS_UPDATE:
85
85
  case MEMBERSHIPS_LS_UPDATE:
86
86
  case SPLIT_KILL:
87
- case RB_SEGMENT_UPDATE:
88
87
  pushEmitter.emit(parsedData.type, parsedData);
89
88
  break;
90
89
 
@@ -1,5 +1,5 @@
1
1
  import { ControlType } from '../constants';
2
- import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE, RB_SEGMENT_UPDATE } from '../types';
2
+ import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../types';
3
3
 
4
4
  export enum Compression {
5
5
  None = 0,
@@ -42,7 +42,7 @@ export interface ISegmentUpdateData {
42
42
  }
43
43
 
44
44
  export interface ISplitUpdateData {
45
- type: SPLIT_UPDATE | RB_SEGMENT_UPDATE,
45
+ type: SPLIT_UPDATE,
46
46
  changeNumber: number,
47
47
  pcn?: number,
48
48
  d?: string,
@@ -1,16 +1,12 @@
1
- import { IRBSegment, ISplit } from '../../../dtos/types';
2
- import { STREAMING_PARSING_SPLIT_UPDATE } from '../../../logger/constants';
1
+ import { ISplit } from '../../../dtos/types';
3
2
  import { ILogger } from '../../../logger/types';
4
3
  import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
5
4
  import { ISplitsEventEmitter } from '../../../readiness/types';
6
- import { IRBSegmentsCacheSync, ISplitsCacheSync, IStorageSync } from '../../../storages/types';
5
+ import { ISplitsCacheSync } from '../../../storages/types';
7
6
  import { ITelemetryTracker } from '../../../trackers/types';
8
7
  import { Backoff } from '../../../utils/Backoff';
9
8
  import { SPLITS } from '../../../utils/constants';
10
9
  import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
11
- import { InstantUpdate } from '../../polling/updaters/splitChangesUpdater';
12
- import { RB_SEGMENT_UPDATE } from '../constants';
13
- import { parseFFUpdatePayload } from '../parseUtils';
14
10
  import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
15
11
  import { FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT, FETCH_BACKOFF_MAX_RETRIES } from './constants';
16
12
  import { IUpdateWorker } from './types';
@@ -18,128 +14,102 @@ import { IUpdateWorker } from './types';
18
14
  /**
19
15
  * SplitsUpdateWorker factory
20
16
  */
21
- export function SplitsUpdateWorker(log: ILogger, storage: IStorageSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, telemetryTracker: ITelemetryTracker, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker<[updateData: ISplitUpdateData]> & { killSplit(event: ISplitKillData): void } {
17
+ export function SplitsUpdateWorker(log: ILogger, splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, telemetryTracker: ITelemetryTracker, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker<[updateData: ISplitUpdateData, payload?: ISplit]> & { killSplit(event: ISplitKillData): void } {
22
18
 
23
- const ff = SplitsUpdateWorker(storage.splits);
24
- const rbs = SplitsUpdateWorker(storage.rbSegments);
19
+ let maxChangeNumber = 0;
20
+ let handleNewEvent = false;
21
+ let isHandlingEvent: boolean;
22
+ let cdnBypass: boolean;
23
+ let payload: ISplit | undefined;
24
+ const backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
25
25
 
26
- function SplitsUpdateWorker(cache: ISplitsCacheSync | IRBSegmentsCacheSync) {
27
- let maxChangeNumber = -1;
28
- let handleNewEvent = false;
29
- let isHandlingEvent: boolean;
30
- let cdnBypass: boolean;
31
- let instantUpdate: InstantUpdate | undefined;
32
- const backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
26
+ function __handleSplitUpdateCall() {
27
+ isHandlingEvent = true;
28
+ if (maxChangeNumber > splitsCache.getChangeNumber()) {
29
+ handleNewEvent = false;
30
+ const splitUpdateNotification = payload ? { payload, changeNumber: maxChangeNumber } : undefined;
31
+ // fetch splits revalidating data if cached
32
+ splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined, splitUpdateNotification).then(() => {
33
+ if (!isHandlingEvent) return; // halt if `stop` has been called
34
+ if (handleNewEvent) {
35
+ __handleSplitUpdateCall();
36
+ } else {
37
+ if (splitUpdateNotification) telemetryTracker.trackUpdatesFromSSE(SPLITS);
38
+ // fetch new registered segments for server-side API. Not retrying on error
39
+ if (segmentsSyncTask) segmentsSyncTask.execute(true);
33
40
 
34
- function __handleSplitUpdateCall() {
35
- isHandlingEvent = true;
36
- if (maxChangeNumber > cache.getChangeNumber()) {
37
- handleNewEvent = false;
38
- // fetch splits revalidating data if cached
39
- splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined, instantUpdate).then(() => {
40
- if (!isHandlingEvent) return; // halt if `stop` has been called
41
- if (handleNewEvent) {
42
- __handleSplitUpdateCall();
43
- } else {
44
- if (instantUpdate) telemetryTracker.trackUpdatesFromSSE(SPLITS);
45
- // fetch new registered segments for server-side API. Not retrying on error
46
- if (segmentsSyncTask) segmentsSyncTask.execute(true);
47
-
48
- const attempts = backoff.attempts + 1;
41
+ const attempts = backoff.attempts + 1;
49
42
 
50
- if (ff.isSync() && rbs.isSync()) {
51
- log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
52
- isHandlingEvent = false;
53
- return;
54
- }
43
+ if (maxChangeNumber <= splitsCache.getChangeNumber()) {
44
+ log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
45
+ isHandlingEvent = false;
46
+ return;
47
+ }
55
48
 
56
- if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
57
- backoff.scheduleCall();
58
- return;
59
- }
49
+ if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
50
+ backoff.scheduleCall();
51
+ return;
52
+ }
60
53
 
61
- if (cdnBypass) {
62
- log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
63
- isHandlingEvent = false;
64
- } else {
65
- backoff.reset();
66
- cdnBypass = true;
67
- __handleSplitUpdateCall();
68
- }
54
+ if (cdnBypass) {
55
+ log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
56
+ isHandlingEvent = false;
57
+ } else {
58
+ backoff.reset();
59
+ cdnBypass = true;
60
+ __handleSplitUpdateCall();
69
61
  }
70
- });
71
- } else {
72
- isHandlingEvent = false;
73
- }
62
+ }
63
+ });
64
+ } else {
65
+ isHandlingEvent = false;
74
66
  }
67
+ }
75
68
 
76
- return {
77
- /**
78
- * Invoked by NotificationProcessor on SPLIT_UPDATE or RB_SEGMENT_UPDATE event
79
- *
80
- * @param changeNumber - change number of the notification
81
- */
82
- put({ changeNumber, pcn, type }: ISplitUpdateData, payload?: ISplit | IRBSegment) {
83
- const currentChangeNumber = cache.getChangeNumber();
69
+ /**
70
+ * Invoked by NotificationProcessor on SPLIT_UPDATE event
71
+ *
72
+ * @param changeNumber - change number of the SPLIT_UPDATE notification
73
+ */
74
+ function put({ changeNumber, pcn }: ISplitUpdateData, _payload?: ISplit) {
75
+ const currentChangeNumber = splitsCache.getChangeNumber();
84
76
 
85
- if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
77
+ if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
86
78
 
87
- maxChangeNumber = changeNumber;
88
- handleNewEvent = true;
89
- cdnBypass = false;
90
- instantUpdate = undefined;
79
+ maxChangeNumber = changeNumber;
80
+ handleNewEvent = true;
81
+ cdnBypass = false;
82
+ payload = undefined;
91
83
 
92
- if (payload && currentChangeNumber === pcn) {
93
- instantUpdate = { payload, changeNumber, type };
94
- }
84
+ if (_payload && currentChangeNumber === pcn) {
85
+ payload = _payload;
86
+ }
95
87
 
96
- if (backoff.timeoutID || !isHandlingEvent) __handleSplitUpdateCall();
97
- backoff.reset();
98
- },
99
- stop() {
100
- isHandlingEvent = false;
101
- backoff.reset();
102
- },
103
- isSync() {
104
- return maxChangeNumber <= cache.getChangeNumber();
105
- }
106
- };
88
+ if (backoff.timeoutID || !isHandlingEvent) __handleSplitUpdateCall();
89
+ backoff.reset();
107
90
  }
108
91
 
109
92
  return {
110
- put(parsedData) {
111
- if (parsedData.d && parsedData.c !== undefined) {
112
- try {
113
- const payload = parseFFUpdatePayload(parsedData.c, parsedData.d);
114
- if (payload) {
115
- (parsedData.type === RB_SEGMENT_UPDATE ? rbs : ff).put(parsedData, payload);
116
- return;
117
- }
118
- } catch (e) {
119
- log.warn(STREAMING_PARSING_SPLIT_UPDATE, [parsedData.type, e]);
120
- }
121
- }
122
- (parsedData.type === RB_SEGMENT_UPDATE ? rbs : ff).put(parsedData);
123
- },
93
+ put,
124
94
  /**
125
95
  * Invoked by NotificationProcessor on SPLIT_KILL event
126
96
  *
127
- * @param changeNumber - change number of the notification
97
+ * @param changeNumber - change number of the SPLIT_UPDATE notification
128
98
  * @param splitName - name of split to kill
129
99
  * @param defaultTreatment - default treatment value
130
100
  */
131
101
  killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
132
- if (storage.splits.killLocally(splitName, defaultTreatment, changeNumber)) {
102
+ if (splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
133
103
  // trigger an SDK_UPDATE if Split was killed locally
134
104
  splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
135
105
  }
136
106
  // queues the SplitChanges fetch (only if changeNumber is newer)
137
- ff.put({ changeNumber } as ISplitUpdateData);
107
+ put({ changeNumber } as ISplitUpdateData);
138
108
  },
139
109
 
140
110
  stop() {
141
- ff.stop();
142
- rbs.stop();
111
+ isHandlingEvent = false;
112
+ backoff.reset();
143
113
  }
144
114
  };
145
115
  }
@@ -30,7 +30,6 @@ export const MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
30
30
  export const SEGMENT_UPDATE = 'SEGMENT_UPDATE';
31
31
  export const SPLIT_KILL = 'SPLIT_KILL';
32
32
  export const SPLIT_UPDATE = 'SPLIT_UPDATE';
33
- export const RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE';
34
33
 
35
34
  // Control-type push notifications, handled by NotificationKeeper
36
35
  export const CONTROL = 'CONTROL';
@@ -2,7 +2,7 @@ import { algorithms } from '../../utils/decompress';
2
2
  import { decodeFromBase64 } from '../../utils/base64';
3
3
  import { hash } from '../../utils/murmur3/murmur3';
4
4
  import { Compression, IMembershipMSUpdateData, KeyList } from './SSEHandler/types';
5
- import { IRBSegment, ISplit } from '../../dtos/types';
5
+ import { ISplit } from '../../dtos/types';
6
6
 
7
7
  const GZIP = 1;
8
8
  const ZLIB = 2;
@@ -82,7 +82,7 @@ export function isInBitmap(bitmap: Uint8Array, hash64hex: string) {
82
82
  /**
83
83
  * Parse feature flags notifications for instant feature flag updates
84
84
  */
85
- export function parseFFUpdatePayload(compression: Compression, data: string): ISplit | IRBSegment | undefined {
85
+ export function parseFFUpdatePayload(compression: Compression, data: string): ISplit | undefined {
86
86
  return compression > 0 ?
87
87
  parseKeyList(data, compression, false) :
88
88
  JSON.parse(decodeFromBase64(data));
@@ -11,10 +11,10 @@ import { authenticateFactory, hashUserKey } from './AuthClient';
11
11
  import { forOwn } from '../../utils/lang';
12
12
  import { SSEClient } from './SSEClient';
13
13
  import { getMatching } from '../../utils/key';
14
- import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, RB_SEGMENT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants';
15
- import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE } from '../../logger/constants';
14
+ import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants';
15
+ import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE, STREAMING_PARSING_SPLIT_UPDATE } from '../../logger/constants';
16
16
  import { IMembershipMSUpdateData, IMembershipLSUpdateData, KeyList, UpdateStrategy } from './SSEHandler/types';
17
- import { getDelay, isInBitmap, parseBitmap, parseKeyList } from './parseUtils';
17
+ import { getDelay, isInBitmap, parseBitmap, parseFFUpdatePayload, parseKeyList } from './parseUtils';
18
18
  import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64';
19
19
  import { IAuthTokenPushEnabled } from './AuthClient/types';
20
20
  import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants';
@@ -56,7 +56,7 @@ export function pushManagerFactory(
56
56
  // MySegmentsUpdateWorker (client-side) are initiated in `add` method
57
57
  const segmentsUpdateWorker = userKey ? undefined : SegmentsUpdateWorker(log, pollingManager.segmentsSyncTask as ISegmentsSyncTask, storage.segments);
58
58
  // For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
59
- const splitsUpdateWorker = SplitsUpdateWorker(log, storage, pollingManager.splitsSyncTask, readiness.splits, telemetryTracker, userKey ? undefined : pollingManager.segmentsSyncTask as ISegmentsSyncTask);
59
+ const splitsUpdateWorker = SplitsUpdateWorker(log, storage.splits, pollingManager.splitsSyncTask, readiness.splits, telemetryTracker, userKey ? undefined : pollingManager.segmentsSyncTask as ISegmentsSyncTask);
60
60
 
61
61
  // [Only for client-side] map of hashes to user keys, to dispatch membership update events to the corresponding MySegmentsUpdateWorker
62
62
  const userKeyHashes: Record<string, string> = {};
@@ -219,8 +219,20 @@ export function pushManagerFactory(
219
219
  /** Functions related to synchronization (Queues and Workers in the spec) */
220
220
 
221
221
  pushEmitter.on(SPLIT_KILL, splitsUpdateWorker.killSplit);
222
- pushEmitter.on(SPLIT_UPDATE, splitsUpdateWorker.put);
223
- pushEmitter.on(RB_SEGMENT_UPDATE, splitsUpdateWorker.put);
222
+ pushEmitter.on(SPLIT_UPDATE, (parsedData) => {
223
+ if (parsedData.d && parsedData.c !== undefined) {
224
+ try {
225
+ const payload = parseFFUpdatePayload(parsedData.c, parsedData.d);
226
+ if (payload) {
227
+ splitsUpdateWorker.put(parsedData, payload);
228
+ return;
229
+ }
230
+ } catch (e) {
231
+ log.warn(STREAMING_PARSING_SPLIT_UPDATE, [e]);
232
+ }
233
+ }
234
+ splitsUpdateWorker.put(parsedData);
235
+ });
224
236
 
225
237
  function handleMySegmentsUpdate(parsedData: IMembershipMSUpdateData | IMembershipLSUpdateData) {
226
238
  switch (parsedData.u) {
@@ -16,19 +16,18 @@ export type MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
16
16
  export type SEGMENT_UPDATE = 'SEGMENT_UPDATE';
17
17
  export type SPLIT_KILL = 'SPLIT_KILL';
18
18
  export type SPLIT_UPDATE = 'SPLIT_UPDATE';
19
- export type RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE';
20
19
 
21
20
  // Control-type push notifications, handled by NotificationKeeper
22
21
  export type CONTROL = 'CONTROL';
23
22
  export type OCCUPANCY = 'OCCUPANCY';
24
23
 
25
- export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MEMBERSHIPS_MS_UPDATE | MEMBERSHIPS_LS_UPDATE | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL | RB_SEGMENT_UPDATE | ControlType.STREAMING_RESET
24
+ export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MEMBERSHIPS_MS_UPDATE | MEMBERSHIPS_LS_UPDATE | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL | ControlType.STREAMING_RESET
26
25
 
27
26
  type IParsedData<T extends IPushEvent> =
28
27
  T extends MEMBERSHIPS_MS_UPDATE ? IMembershipMSUpdateData :
29
28
  T extends MEMBERSHIPS_LS_UPDATE ? IMembershipLSUpdateData :
30
29
  T extends SEGMENT_UPDATE ? ISegmentUpdateData :
31
- T extends SPLIT_UPDATE | RB_SEGMENT_UPDATE ? ISplitUpdateData :
30
+ T extends SPLIT_UPDATE ? ISplitUpdateData :
32
31
  T extends SPLIT_KILL ? ISplitKillData : INotificationData;
33
32
 
34
33
  /**
@@ -155,14 +155,14 @@ export function syncManagerOnlineFactory(
155
155
  if (pushManager) {
156
156
  if (pollingManager.isRunning()) {
157
157
  // if doing polling, we must start the periodic fetch of data
158
- if (storage.splits.usesSegments() || storage.rbSegments.usesSegments()) mySegmentsSyncTask.start();
158
+ if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
159
159
  } else {
160
160
  // if not polling, we must execute the sync task for the initial fetch
161
161
  // of segments since `syncAll` was already executed when starting the main client
162
162
  mySegmentsSyncTask.execute();
163
163
  }
164
164
  } else {
165
- if (storage.splits.usesSegments() || storage.rbSegments.usesSegments()) mySegmentsSyncTask.start();
165
+ if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
166
166
  }
167
167
  } else {
168
168
  if (!readinessManager.isReady()) mySegmentsSyncTask.execute();
@@ -104,13 +104,8 @@ export const DISABLED = 0;
104
104
  export const ENABLED = 1;
105
105
  export const PAUSED = 2;
106
106
 
107
- export const FLAG_SPEC_VERSION = '1.3';
107
+ export const FLAG_SPEC_VERSION = '1.2';
108
108
 
109
109
  // Matcher types
110
110
  export const IN_SEGMENT = 'IN_SEGMENT';
111
111
  export const IN_LARGE_SEGMENT = 'IN_LARGE_SEGMENT';
112
- export const IN_RULE_BASED_SEGMENT = 'IN_RULE_BASED_SEGMENT';
113
-
114
- export const STANDARD_SEGMENT = 'standard';
115
- export const LARGE_SEGMENT = 'large';
116
- export const RULE_BASED_SEGMENT = 'rule-based';
@@ -111,7 +111,7 @@ export function groupBy<T extends Record<string, any>>(source: T[], prop: string
111
111
  /**
112
112
  * Checks if a given value is a boolean.
113
113
  */
114
- export function isBoolean(val: any): val is boolean {
114
+ export function isBoolean(val: any): boolean {
115
115
  return val === true || val === false;
116
116
  }
117
117