@splitsoftware/splitio-commons 1.16.1-rc.10 → 1.16.1-rc.11

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 (66) hide show
  1. package/cjs/logger/constants.js +2 -2
  2. package/cjs/logger/messages/warn.js +1 -1
  3. package/cjs/services/splitApi.js +1 -1
  4. package/cjs/storages/AbstractSegmentsCacheSync.js +41 -7
  5. package/cjs/storages/dataLoader.js +1 -1
  6. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +19 -63
  7. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +5 -40
  8. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +7 -17
  9. package/cjs/sync/streaming/AuthClient/index.js +1 -1
  10. package/cjs/sync/streaming/SSEHandler/index.js +5 -7
  11. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +8 -6
  12. package/cjs/sync/streaming/constants.js +3 -3
  13. package/cjs/sync/streaming/pushManager.js +21 -24
  14. package/cjs/utils/constants/index.js +3 -4
  15. package/esm/logger/constants.js +1 -1
  16. package/esm/logger/messages/warn.js +1 -1
  17. package/esm/services/splitApi.js +2 -2
  18. package/esm/storages/AbstractSegmentsCacheSync.js +41 -7
  19. package/esm/storages/dataLoader.js +1 -1
  20. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +19 -63
  21. package/esm/storages/inMemory/MySegmentsCacheInMemory.js +5 -40
  22. package/esm/sync/polling/updaters/mySegmentsUpdater.js +7 -17
  23. package/esm/sync/streaming/AuthClient/index.js +1 -1
  24. package/esm/sync/streaming/SSEHandler/index.js +6 -8
  25. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +8 -6
  26. package/esm/sync/streaming/constants.js +2 -2
  27. package/esm/sync/streaming/pushManager.js +24 -27
  28. package/esm/utils/constants/index.js +1 -2
  29. package/package.json +1 -1
  30. package/src/dtos/types.ts +9 -12
  31. package/src/logger/constants.ts +1 -1
  32. package/src/logger/messages/warn.ts +1 -1
  33. package/src/services/splitApi.ts +2 -2
  34. package/src/storages/AbstractSegmentsCacheSync.ts +52 -7
  35. package/src/storages/AbstractSplitsCacheSync.ts +1 -1
  36. package/src/storages/dataLoader.ts +1 -1
  37. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +15 -69
  38. package/src/storages/inMemory/MySegmentsCacheInMemory.ts +6 -46
  39. package/src/storages/types.ts +5 -4
  40. package/src/sync/polling/types.ts +8 -9
  41. package/src/sync/polling/updaters/mySegmentsUpdater.ts +9 -14
  42. package/src/sync/streaming/AuthClient/index.ts +1 -1
  43. package/src/sync/streaming/SSEHandler/index.ts +9 -11
  44. package/src/sync/streaming/SSEHandler/types.ts +6 -6
  45. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +8 -7
  46. package/src/sync/streaming/constants.ts +2 -2
  47. package/src/sync/streaming/parseUtils.ts +2 -2
  48. package/src/sync/streaming/pushManager.ts +26 -29
  49. package/src/sync/streaming/types.ts +6 -6
  50. package/src/sync/submitters/types.ts +3 -4
  51. package/src/utils/constants/index.ts +1 -2
  52. package/types/dtos/types.d.ts +8 -12
  53. package/types/logger/constants.d.ts +1 -1
  54. package/types/storages/AbstractSegmentsCacheSync.d.ts +8 -6
  55. package/types/storages/AbstractSplitsCacheSync.d.ts +1 -1
  56. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +1 -12
  57. package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +1 -9
  58. package/types/storages/types.d.ts +5 -4
  59. package/types/sync/polling/types.d.ts +8 -6
  60. package/types/sync/streaming/SSEHandler/types.d.ts +6 -6
  61. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +1 -2
  62. package/types/sync/streaming/constants.d.ts +2 -2
  63. package/types/sync/streaming/parseUtils.d.ts +2 -2
  64. package/types/sync/streaming/types.d.ts +5 -5
  65. package/types/sync/submitters/types.d.ts +3 -4
  66. package/types/utils/constants/index.d.ts +1 -2
@@ -1,5 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  /* eslint-disable no-unused-vars */
3
+ import { IMySegmentsResponse } from '../dtos/types';
4
+ import { MySegmentsData } from '../sync/polling/types';
3
5
  import { ISegmentsCacheSync } from './types';
4
6
 
5
7
  /**
@@ -28,7 +30,9 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
28
30
  /**
29
31
  * clear the cache.
30
32
  */
31
- abstract clear(): void
33
+ clear() {
34
+ this.resetSegments({});
35
+ }
32
36
 
33
37
  /**
34
38
  * For server-side synchronizer: add the given list of segments to the cache, with an empty list of keys. The segments that already exist are not modified.
@@ -49,16 +53,57 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
49
53
  abstract getKeysCount(): number
50
54
 
51
55
  /**
52
- * For server-side synchronizer: set the change number of `name` segment.
53
- * For client-side synchronizer: the method is not used.
56
+ * For server-side synchronizer: change number of `name` segment.
57
+ * For client-side synchronizer: change number of mySegments.
54
58
  */
55
- setChangeNumber(name: string, changeNumber: number): boolean { return true; }
56
-
59
+ abstract setChangeNumber(name?: string, changeNumber?: number): boolean | void
57
60
  abstract getChangeNumber(name: string): number
58
61
 
59
62
  /**
60
63
  * For server-side synchronizer: the method is not used.
61
- * For client-side synchronizer: reset the cache with the given list of segments.
64
+ * For client-side synchronizer: it resets or updates the cache.
62
65
  */
63
- resetSegments(names: string[], changeNumber?: number): boolean { return true; }
66
+ resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean {
67
+ this.setChangeNumber(undefined, segmentsData.cn);
68
+
69
+ const { added, removed } = segmentsData as MySegmentsData;
70
+
71
+ if (added && removed) {
72
+ let isDiff = false;
73
+
74
+ added.forEach(segment => {
75
+ isDiff = this.addToSegment(segment) || isDiff;
76
+ });
77
+
78
+ removed.forEach(segment => {
79
+ isDiff = this.removeFromSegment(segment) || isDiff;
80
+ });
81
+
82
+ return isDiff;
83
+ }
84
+
85
+ const names = ((segmentsData as IMySegmentsResponse).k || []).map(s => s.n).sort();
86
+ const storedSegmentKeys = this.getRegisteredSegments().sort();
87
+
88
+ // Extreme fast => everything is empty
89
+ if (!names.length && !storedSegmentKeys.length) return false;
90
+
91
+ let index = 0;
92
+
93
+ while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index]) index++;
94
+
95
+ // Quick path => no changes
96
+ if (index === names.length && index === storedSegmentKeys.length) return false;
97
+
98
+ // Slowest path => add and/or remove segments
99
+ for (let removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
100
+ this.removeFromSegment(storedSegmentKeys[removeIndex]);
101
+ }
102
+
103
+ for (let addIndex = index; addIndex < names.length; addIndex++) {
104
+ this.addToSegment(names[addIndex]);
105
+ }
106
+
107
+ return true;
108
+ }
64
109
  }
@@ -32,7 +32,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
32
32
  return splits;
33
33
  }
34
34
 
35
- abstract setChangeNumber(changeNumber: number): boolean
35
+ abstract setChangeNumber(changeNumber: number): boolean | void
36
36
 
37
37
  abstract getChangeNumber(): number
38
38
 
@@ -50,6 +50,6 @@ export function dataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoa
50
50
  return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
51
51
  });
52
52
  }
53
- storage.segments.resetSegments(mySegmentsData);
53
+ storage.segments.resetSegments({ k: mySegmentsData.map(s => ({ n: s })) });
54
54
  };
55
55
  }
@@ -16,22 +16,11 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
16
16
  // There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
17
17
  }
18
18
 
19
- /**
20
- * Removes list of segments from localStorage
21
- * @NOTE this method is not being used at the moment.
22
- */
23
- clear() {
24
- this.log.info(LOG_PREFIX + 'Flushing MySegments data from localStorage');
25
-
26
- // We cannot simply call `localStorage.clear()` since that implies removing user items from the storage
27
- // We could optimize next sentence, since it implies iterating over all localStorage items
28
- this.resetSegments([]);
29
- }
30
-
31
19
  addToSegment(name: string): boolean {
32
20
  const segmentKey = this.keys.buildSegmentNameKey(name);
33
21
 
34
22
  try {
23
+ if (localStorage.getItem(segmentKey) === DEFINED) return false;
35
24
  localStorage.setItem(segmentKey, DEFINED);
36
25
  return true;
37
26
  } catch (e) {
@@ -44,6 +33,7 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
44
33
  const segmentKey = this.keys.buildSegmentNameKey(name);
45
34
 
46
35
  try {
36
+ if (localStorage.getItem(segmentKey) !== DEFINED) return false;
47
37
  localStorage.removeItem(segmentKey);
48
38
  return true;
49
39
  } catch (e) {
@@ -56,41 +46,22 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
56
46
  return localStorage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
57
47
  }
58
48
 
59
- /**
60
- * Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
61
- *
62
- * @param {string[]} names list of segment names
63
- * @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
64
- */
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
-
76
- let isDiff = false;
77
- let index;
78
-
49
+ getRegisteredSegments(): string[] {
79
50
  // Scan current values from localStorage
80
- const storedSegmentNames = Object.keys(localStorage).reduce((accum, key) => {
51
+ return Object.keys(localStorage).reduce((accum, key) => {
81
52
  let segmentName = this.keys.extractSegmentName(key);
82
53
 
83
54
  if (segmentName) {
84
55
  accum.push(segmentName);
85
56
  } else {
86
- // @TODO @BREAKING: This is only to clean up "old" keys. Remove this whole else code block and reuse `getRegisteredSegments` method.
57
+ // @TODO @BREAKING: This is only to clean up "old" keys. Remove this whole else code block
87
58
  segmentName = this.keys.extractOldSegmentKey(key);
88
59
 
89
60
  if (segmentName) { // this was an old segment key, let's clean up.
90
61
  const newSegmentKey = this.keys.buildSegmentNameKey(segmentName);
91
62
  try {
92
63
  // If the new format key is not there, create it.
93
- if (!localStorage.getItem(newSegmentKey) && names.indexOf(segmentName) > -1) {
64
+ if (!localStorage.getItem(newSegmentKey)) {
94
65
  localStorage.setItem(newSegmentKey, DEFINED);
95
66
  // we are migrating a segment, let's track it.
96
67
  accum.push(segmentName);
@@ -104,46 +75,21 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
104
75
 
105
76
  return accum;
106
77
  }, [] as string[]);
107
-
108
- // Extreme fast => everything is empty
109
- if (names.length === 0 && storedSegmentNames.length === names.length)
110
- return isDiff;
111
-
112
- // Quick path
113
- if (storedSegmentNames.length !== names.length) {
114
- isDiff = true;
115
-
116
- storedSegmentNames.forEach(name => this.removeFromSegment(name));
117
- names.forEach(name => this.addToSegment(name));
118
- } else {
119
- // Slowest path => we need to find at least 1 difference because
120
- for (index = 0; index < names.length && storedSegmentNames.indexOf(names[index]) !== -1; index++) {
121
- // TODO: why empty statement?
122
- }
123
-
124
- if (index < names.length) {
125
- isDiff = true;
126
-
127
- storedSegmentNames.forEach(name => this.removeFromSegment(name));
128
- names.forEach(name => this.addToSegment(name));
129
- }
130
- }
131
-
132
- return isDiff;
133
- }
134
-
135
- getRegisteredSegments(): string[] {
136
- return Object.keys(localStorage).reduce<string[]>((accum, key) => {
137
- const segmentName = this.keys.extractSegmentName(key);
138
- if (segmentName) accum.push(segmentName);
139
- return accum;
140
- }, []);
141
78
  }
142
79
 
143
80
  getKeysCount() {
144
81
  return 1;
145
82
  }
146
83
 
84
+ setChangeNumber(name?: string, changeNumber?: number) {
85
+ try {
86
+ if (changeNumber) localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
87
+ else localStorage.removeItem(this.keys.buildTillKey());
88
+ } catch (e) {
89
+ this.log.error(e);
90
+ }
91
+ }
92
+
147
93
  getChangeNumber() {
148
94
  const n = -1;
149
95
  let value: string | number | null = localStorage.getItem(this.keys.buildTillKey());
@@ -9,17 +9,17 @@ export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
9
9
  private segmentCache: Record<string, boolean> = {};
10
10
  private cn?: number;
11
11
 
12
- clear() {
13
- this.segmentCache = {};
14
- }
15
-
16
12
  addToSegment(name: string): boolean {
13
+ if (this.segmentCache[name]) return false;
14
+
17
15
  this.segmentCache[name] = true;
18
16
 
19
17
  return true;
20
18
  }
21
19
 
22
20
  removeFromSegment(name: string): boolean {
21
+ if (!this.segmentCache[name]) return false;
22
+
23
23
  delete this.segmentCache[name];
24
24
 
25
25
  return true;
@@ -29,49 +29,9 @@ export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
29
29
  return this.segmentCache[name] === true;
30
30
  }
31
31
 
32
- /**
33
- * Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
34
- * @NOTE based on the way we use segments in the browser, this way is the best option
35
- *
36
- * @param {string[]} names list of segment names
37
- * @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
38
- */
39
- resetSegments(names: string[], changeNumber?: number): boolean {
40
- this.cn = changeNumber;
41
- let isDiff = false;
42
- let index;
43
-
44
- const storedSegmentKeys = Object.keys(this.segmentCache);
45
32
 
46
- // Extreme fast => everything is empty
47
- if (names.length === 0 && storedSegmentKeys.length === names.length)
48
- return isDiff;
49
-
50
- // Quick path
51
- if (storedSegmentKeys.length !== names.length) {
52
- isDiff = true;
53
-
54
- this.segmentCache = {};
55
- names.forEach(s => {
56
- this.addToSegment(s);
57
- });
58
- } else {
59
- // Slowest path => we need to find at least 1 difference because
60
- for (index = 0; index < names.length && this.isInSegment(names[index]); index++) {
61
- // TODO: why empty statement?
62
- }
63
-
64
- if (index < names.length) {
65
- isDiff = true;
66
-
67
- this.segmentCache = {};
68
- names.forEach(s => {
69
- this.addToSegment(s);
70
- });
71
- }
72
- }
73
-
74
- return isDiff;
33
+ setChangeNumber(name?: string, changeNumber?: number) {
34
+ this.cn = changeNumber;
75
35
  }
76
36
 
77
37
  getChangeNumber() {
@@ -1,4 +1,5 @@
1
- import { MaybeThenable, ISplit } from '../dtos/types';
1
+ import { MaybeThenable, ISplit, IMySegmentsResponse } from '../dtos/types';
2
+ import { MySegmentsData } from '../sync/polling/types';
2
3
  import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs, TelemetryUsageStatsPayload, UpdatesFromSSEEnum } from '../sync/submitters/types';
3
4
  import { SplitIO, ImpressionDTO, ISettings } from '../types';
4
5
  import { ISet } from '../utils/lang/sets';
@@ -218,7 +219,7 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
218
219
  removeSplits(names: string[]): boolean[],
219
220
  getSplit(name: string): ISplit | null,
220
221
  getSplits(names: string[]): Record<string, ISplit | null>,
221
- setChangeNumber(changeNumber: number): boolean,
222
+ setChangeNumber(changeNumber: number): boolean | void,
222
223
  getChangeNumber(): number,
223
224
  getAll(): ISplit[],
224
225
  getSplitNames(): string[],
@@ -268,9 +269,9 @@ export interface ISegmentsCacheSync extends ISegmentsCacheBase {
268
269
  registerSegments(names: string[]): boolean
269
270
  getRegisteredSegments(): string[]
270
271
  getKeysCount(): number // only used for telemetry
271
- setChangeNumber(name: string, changeNumber: number): boolean
272
+ setChangeNumber(name: string, changeNumber: number): boolean | void
272
273
  getChangeNumber(name: string): number
273
- resetSegments(names: string[], changeNumber?: number): boolean // only for Sync Client-Side
274
+ resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean // only for Sync Client-Side
274
275
  clear(): void
275
276
  }
276
277
 
@@ -1,20 +1,19 @@
1
- import { IMembershipsResponse, ISplit } from '../../dtos/types';
1
+ import { ISplit } from '../../dtos/types';
2
2
  import { IReadinessManager } from '../../readiness/types';
3
3
  import { IStorageSync } from '../../storages/types';
4
+ import { MEMBERSHIP_LS_UPDATE, MEMBERSHIP_MS_UPDATE } from '../streaming/types';
4
5
  import { ITask, ISyncTask } from '../types';
5
6
 
6
7
  export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }], boolean> { }
7
8
 
8
9
  export interface ISegmentsSyncTask extends ISyncTask<[fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number], boolean> { }
9
10
 
10
- export type MySegmentsData = IMembershipsResponse | {
11
- /* segment type */
12
- isLS?: boolean
13
- /* segment name */
14
- name: string
15
- /* action: `true` for add, and `false` for delete */
16
- add: boolean
17
- }[]
11
+ export type MySegmentsData = {
12
+ type: MEMBERSHIP_MS_UPDATE | MEMBERSHIP_LS_UPDATE
13
+ cn: number
14
+ added: string[]
15
+ removed: string[]
16
+ }
18
17
 
19
18
  export interface IMySegmentsSyncTask extends ISyncTask<[segmentsData?: MySegmentsData, noCache?: boolean], boolean> { }
20
19
 
@@ -6,6 +6,8 @@ import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
6
6
  import { ILogger } from '../../../logger/types';
7
7
  import { SYNC_MYSEGMENTS_FETCH_RETRY } from '../../../logger/constants';
8
8
  import { MySegmentsData } from '../types';
9
+ import { IMembershipsResponse } from '../../../dtos/types';
10
+ import { MEMBERSHIP_LS_UPDATE } from '../../streaming/constants';
9
11
 
10
12
  type IMySegmentsUpdater = (segmentList?: MySegmentsData, noCache?: boolean) => Promise<boolean>
11
13
 
@@ -36,23 +38,16 @@ export function mySegmentsUpdaterFactory(
36
38
  }
37
39
 
38
40
  // @TODO if allowing pluggable storages, handle async execution
39
- function updateSegments(segmentsData: MySegmentsData) {
41
+ function updateSegments(segmentsData: IMembershipsResponse | MySegmentsData) {
40
42
 
41
43
  let shouldNotifyUpdate;
42
- if (Array.isArray(segmentsData)) {
43
- // Add/Delete the segment names
44
- segmentsData.forEach(({ isLS, name, add }) => {
45
- const cache = isLS ? largeSegments : segments;
46
- if (cache!.isInSegment(name) !== add) {
47
- shouldNotifyUpdate = true;
48
- if (add) cache!.addToSegment(name);
49
- else cache!.removeFromSegment(name);
50
- }
51
- });
44
+ if ((segmentsData as MySegmentsData).type !== undefined) {
45
+ shouldNotifyUpdate = (segmentsData as MySegmentsData).type === MEMBERSHIP_LS_UPDATE ?
46
+ largeSegments!.resetSegments(segmentsData as MySegmentsData) :
47
+ segments.resetSegments(segmentsData as MySegmentsData);
52
48
  } else {
53
- // Reset the list of segment names
54
- shouldNotifyUpdate = segments.resetSegments((segmentsData.ms?.k || []).map((segment) => segment.n), segmentsData.ms?.cn);
55
- shouldNotifyUpdate = largeSegments!.resetSegments((segmentsData.ls?.k || []).map((segment) => segment.n), segmentsData.ls?.cn) || shouldNotifyUpdate;
49
+ shouldNotifyUpdate = segments.resetSegments((segmentsData as IMembershipsResponse).ms || {});
50
+ shouldNotifyUpdate = largeSegments!.resetSegments((segmentsData as IMembershipsResponse).ls || {}) || shouldNotifyUpdate;
56
51
  }
57
52
 
58
53
  // Notify update if required
@@ -14,7 +14,7 @@ export function authenticateFactory(fetchAuth: IFetchAuth): IAuthenticate {
14
14
 
15
15
  /**
16
16
  * Run authentication requests to Auth Server, and returns a promise that resolves with the decoded JTW token.
17
- * @param {string[] | undefined} userKeys set of user Keys to track MY_SEGMENTS_CHANGES. It is undefined for server-side API.
17
+ * @param {string[] | undefined} userKeys set of user Keys to track membership updates. It is undefined for server-side API.
18
18
  */
19
19
  return function authenticate(userKeys?: string[]): Promise<IAuthToken> {
20
20
  return fetchAuth(userKeys)
@@ -1,9 +1,9 @@
1
1
  import { errorParser, messageParser } from './NotificationParser';
2
2
  import { notificationKeeperFactory } from './NotificationKeeper';
3
- import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, MY_SEGMENTS_UPDATE_V3, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MY_LARGE_SEGMENTS_UPDATE } from '../constants';
3
+ import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIP_MS_UPDATE, MEMBERSHIP_LS_UPDATE } from '../constants';
4
4
  import { IPushEventEmitter } from '../types';
5
5
  import { ISseEventHandler } from '../SSEClient/types';
6
- import { IControlData, INotificationError, INotificationMessage, IOccupancyData } from './types';
6
+ import { INotificationError, INotificationMessage } from './types';
7
7
  import { ILogger } from '../../../logger/types';
8
8
  import { STREAMING_PARSING_ERROR_FAILS, ERROR_STREAMING_SSE, STREAMING_PARSING_MESSAGE_FAILS, STREAMING_NEW_MESSAGE } from '../../../logger/constants';
9
9
  import { ABLY_ERROR, NON_REQUESTED, SSE_CONNECTION_ERROR } from '../../../utils/constants';
@@ -75,26 +75,24 @@ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter,
75
75
  log.debug(STREAMING_NEW_MESSAGE, [data]);
76
76
 
77
77
  // we only handle update events if streaming is up
78
- // @ts-expect-error
79
- const type = parsedData.type || parsedData.t;
80
- if (!notificationKeeper.isStreamingUp() && [OCCUPANCY, CONTROL].indexOf(type) === -1) return;
78
+ if (!notificationKeeper.isStreamingUp() && [OCCUPANCY, CONTROL].indexOf(parsedData.type) === -1) return;
81
79
 
82
- switch (type) {
80
+ switch (parsedData.type) {
83
81
  /* update events */
84
82
  case SPLIT_UPDATE:
85
83
  case SEGMENT_UPDATE:
86
- case MY_SEGMENTS_UPDATE_V3:
87
- case MY_LARGE_SEGMENTS_UPDATE:
84
+ case MEMBERSHIP_MS_UPDATE:
85
+ case MEMBERSHIP_LS_UPDATE:
88
86
  case SPLIT_KILL:
89
- pushEmitter.emit(type, parsedData);
87
+ pushEmitter.emit(parsedData.type, parsedData);
90
88
  break;
91
89
 
92
90
  /* occupancy & control events, handled by NotificationManagerKeeper */
93
91
  case OCCUPANCY:
94
- notificationKeeper.handleOccupancyEvent((parsedData as IOccupancyData).metrics.publishers, channel, timestamp);
92
+ notificationKeeper.handleOccupancyEvent(parsedData.metrics.publishers, channel, timestamp);
95
93
  break;
96
94
  case CONTROL:
97
- notificationKeeper.handleControlEvent((parsedData as IControlData).controlType, channel, timestamp);
95
+ notificationKeeper.handleControlEvent(parsedData.controlType, channel, timestamp);
98
96
  break;
99
97
 
100
98
  default:
@@ -1,5 +1,5 @@
1
1
  import { ControlType } from '../constants';
2
- import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MY_LARGE_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V3 } from '../types';
2
+ import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIP_LS_UPDATE, MEMBERSHIP_MS_UPDATE } from '../types';
3
3
 
4
4
  export enum Compression {
5
5
  None = 0,
@@ -19,8 +19,8 @@ export interface KeyList {
19
19
  r?: string[], // decimal hash64 of user keys
20
20
  }
21
21
 
22
- interface IMySegmentsUpdateData<T extends string> {
23
- t: T,
22
+ interface IMembershipUpdateData<T extends string> {
23
+ type: T,
24
24
  cn: number,
25
25
  n?: string[],
26
26
  c?: Compression,
@@ -31,9 +31,9 @@ interface IMySegmentsUpdateData<T extends string> {
31
31
  s?: number, // seed for hash function
32
32
  }
33
33
 
34
- export interface IMySegmentsUpdateV3Data extends IMySegmentsUpdateData<MY_SEGMENTS_UPDATE_V3> { }
34
+ export interface IMembershipMSUpdateData extends IMembershipUpdateData<MEMBERSHIP_MS_UPDATE> { }
35
35
 
36
- export interface IMyLargeSegmentsUpdateData extends IMySegmentsUpdateData<MY_LARGE_SEGMENTS_UPDATE> { }
36
+ export interface IMembershipLSUpdateData extends IMembershipUpdateData<MEMBERSHIP_LS_UPDATE> { }
37
37
 
38
38
  export interface ISegmentUpdateData {
39
39
  type: SEGMENT_UPDATE,
@@ -68,6 +68,6 @@ export interface IOccupancyData {
68
68
  }
69
69
  }
70
70
 
71
- export type INotificationData = IMySegmentsUpdateV3Data | IMyLargeSegmentsUpdateData | ISegmentUpdateData | ISplitUpdateData | ISplitKillData | IControlData | IOccupancyData
71
+ export type INotificationData = IMembershipMSUpdateData | IMembershipLSUpdateData | ISegmentUpdateData | ISplitUpdateData | ISplitKillData | IControlData | IOccupancyData
72
72
  export type INotificationMessage = { parsedData: INotificationData, channel: string, timestamp: number, data: string }
73
73
  export type INotificationError = Event & { parsedData?: any, message?: string }
@@ -2,12 +2,12 @@ import { IMySegmentsSyncTask, MySegmentsData } from '../../polling/types';
2
2
  import { Backoff } from '../../../utils/Backoff';
3
3
  import { IUpdateWorker } from './types';
4
4
  import { ITelemetryTracker } from '../../../trackers/types';
5
- import { UpdatesFromSSEEnum } from '../../submitters/types';
5
+ import { MEMBERSHIPS } from '../../../utils/constants';
6
6
 
7
7
  /**
8
8
  * MySegmentsUpdateWorker factory
9
9
  */
10
- export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask, telemetryTracker: ITelemetryTracker, updateType: UpdatesFromSSEEnum): IUpdateWorker<[changeNumber: number, segmentsData?: MySegmentsData, delay?: number]> {
10
+ export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask, telemetryTracker: ITelemetryTracker): IUpdateWorker<[mySegmentsData?: Pick<MySegmentsData, 'type' | 'cn'>, payload?: Pick<MySegmentsData, 'added' | 'removed'>, delay?: number]> {
11
11
 
12
12
  let maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
13
13
  let currentChangeNumber = -1;
@@ -37,7 +37,7 @@ export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask,
37
37
  syncTask.then((result) => {
38
38
  if (!isHandlingEvent) return; // halt if `stop` has been called
39
39
  if (result !== false) { // Unlike `Splits|SegmentsUpdateWorker`, we cannot use `mySegmentsCache.getChangeNumber` since `/mySegments` endpoint doesn't provide this value.
40
- if (_segmentsData) telemetryTracker.trackUpdatesFromSSE(updateType);
40
+ if (_segmentsData) telemetryTracker.trackUpdatesFromSSE(MEMBERSHIPS);
41
41
  currentChangeNumber = Math.max(currentChangeNumber, currentMaxChangeNumber); // use `currentMaxChangeNumber`, in case that `maxChangeNumber` was updated during fetch.
42
42
  }
43
43
  if (handleNewEvent) {
@@ -59,13 +59,14 @@ export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask,
59
59
  * @param segmentsData data for KeyList or SegmentRemoval instant updates
60
60
  * @param delay optional time to wait for BoundedFetchRequest or BoundedFetchRequest updates
61
61
  */
62
- put(changeNumber: number, segmentsData?: MySegmentsData, delay?: number) {
62
+ put(mySegmentsData: Pick<MySegmentsData, 'type' | 'cn'>, payload?: Pick<MySegmentsData, 'added' | 'removed'>, delay?: number) {
63
+ const { type, cn } = mySegmentsData;
63
64
  // Ignore event if it is outdated or if there is a pending fetch request (_delay is set)
64
- if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber || _delay) return;
65
+ if (cn <= currentChangeNumber || cn <= maxChangeNumber || _delay) return;
65
66
 
66
- maxChangeNumber = changeNumber;
67
+ maxChangeNumber = cn;
67
68
  handleNewEvent = true;
68
- _segmentsData = segmentsData;
69
+ _segmentsData = payload && { type, cn, added: payload.added, removed: payload.removed };
69
70
  _delay = delay;
70
71
 
71
72
  if (backoff.timeoutID || !isHandlingEvent) __handleMySegmentsUpdateCall();
@@ -25,11 +25,11 @@ export const PUSH_SUBSYSTEM_UP = 'PUSH_SUBSYSTEM_UP';
25
25
  export const PUSH_SUBSYSTEM_DOWN = 'PUSH_SUBSYSTEM_DOWN';
26
26
 
27
27
  // Update-type push notifications, handled by NotificationProcessor
28
- export const MY_SEGMENTS_UPDATE_V3 = 'MY_SEGMENTS_UPDATE_V3';
28
+ export const MEMBERSHIP_MS_UPDATE = 'MEMBERSHIP_MS_UPDATE';
29
+ export const MEMBERSHIP_LS_UPDATE = 'MEMBERSHIP_LS_UPDATE';
29
30
  export const SEGMENT_UPDATE = 'SEGMENT_UPDATE';
30
31
  export const SPLIT_KILL = 'SPLIT_KILL';
31
32
  export const SPLIT_UPDATE = 'SPLIT_UPDATE';
32
- export const MY_LARGE_SEGMENTS_UPDATE = 'MY_LARGE_SEGMENTS_UPDATE';
33
33
 
34
34
  // Control-type push notifications, handled by NotificationKeeper
35
35
  export const CONTROL = 'CONTROL';
@@ -1,7 +1,7 @@
1
1
  import { algorithms } from '../../utils/decompress';
2
2
  import { decodeFromBase64 } from '../../utils/base64';
3
3
  import { hash } from '../../utils/murmur3/murmur3';
4
- import { Compression, IMyLargeSegmentsUpdateData, KeyList } from './SSEHandler/types';
4
+ import { Compression, IMembershipMSUpdateData, KeyList } from './SSEHandler/types';
5
5
  import { ISplit } from '../../dtos/types';
6
6
 
7
7
  const GZIP = 1;
@@ -91,7 +91,7 @@ export function parseFFUpdatePayload(compression: Compression, data: string): IS
91
91
 
92
92
  const DEFAULT_MAX_INTERVAL = 60000;
93
93
 
94
- export function getDelay(parsedData: Pick<IMyLargeSegmentsUpdateData, 'i' | 'h' | 's'>, matchingKey: string) {
94
+ export function getDelay(parsedData: Pick<IMembershipMSUpdateData, 'i' | 'h' | 's'>, matchingKey: string) {
95
95
  if (parsedData.h === 0) return 0;
96
96
 
97
97
  const interval = parsedData.i || DEFAULT_MAX_INTERVAL;