@splitsoftware/splitio-commons 1.16.1-rc.10 → 1.16.1-rc.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/logger/constants.js +2 -2
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/services/splitApi.js +3 -3
- package/cjs/storages/AbstractSegmentsCacheSync.js +41 -7
- package/cjs/storages/dataLoader.js +1 -1
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +19 -63
- package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +5 -40
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +1 -1
- package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +2 -2
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +12 -21
- package/cjs/sync/streaming/AuthClient/index.js +1 -1
- package/cjs/sync/streaming/SSEHandler/index.js +5 -7
- package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +106 -63
- package/cjs/sync/streaming/constants.js +3 -3
- package/cjs/sync/streaming/pushManager.js +25 -31
- package/cjs/utils/constants/index.js +3 -4
- package/esm/logger/constants.js +1 -1
- package/esm/logger/messages/warn.js +1 -1
- package/esm/services/splitApi.js +4 -4
- package/esm/storages/AbstractSegmentsCacheSync.js +41 -7
- package/esm/storages/dataLoader.js +1 -1
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +19 -63
- package/esm/storages/inMemory/MySegmentsCacheInMemory.js +5 -40
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +1 -1
- package/esm/sync/polling/fetchers/mySegmentsFetcher.js +2 -2
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +12 -21
- package/esm/sync/streaming/AuthClient/index.js +1 -1
- package/esm/sync/streaming/SSEHandler/index.js +6 -8
- package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +106 -63
- package/esm/sync/streaming/constants.js +2 -2
- package/esm/sync/streaming/pushManager.js +28 -34
- package/esm/utils/constants/index.js +1 -2
- package/package.json +1 -1
- package/src/dtos/types.ts +9 -12
- package/src/logger/constants.ts +1 -1
- package/src/logger/messages/warn.ts +1 -1
- package/src/services/splitApi.ts +4 -4
- package/src/services/types.ts +1 -1
- package/src/storages/AbstractSegmentsCacheSync.ts +52 -7
- package/src/storages/AbstractSplitsCacheSync.ts +1 -1
- package/src/storages/dataLoader.ts +1 -1
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +15 -69
- package/src/storages/inMemory/MySegmentsCacheInMemory.ts +6 -46
- package/src/storages/inMemory/TelemetryCacheInMemory.ts +1 -1
- package/src/storages/types.ts +6 -5
- package/src/sync/polling/fetchers/mySegmentsFetcher.ts +2 -1
- package/src/sync/polling/fetchers/types.ts +1 -0
- package/src/sync/polling/types.ts +9 -10
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +15 -19
- package/src/sync/streaming/AuthClient/index.ts +1 -1
- package/src/sync/streaming/SSEHandler/index.ts +9 -11
- package/src/sync/streaming/SSEHandler/types.ts +6 -6
- package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +114 -65
- package/src/sync/streaming/constants.ts +2 -2
- package/src/sync/streaming/parseUtils.ts +2 -2
- package/src/sync/streaming/pushManager.ts +30 -39
- package/src/sync/streaming/types.ts +6 -6
- package/src/sync/submitters/types.ts +4 -5
- package/src/utils/constants/index.ts +1 -2
- package/types/dtos/types.d.ts +8 -12
- package/types/logger/constants.d.ts +1 -1
- package/types/services/types.d.ts +1 -1
- package/types/storages/AbstractSegmentsCacheSync.d.ts +8 -6
- package/types/storages/AbstractSplitsCacheSync.d.ts +1 -1
- package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +1 -12
- package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +1 -9
- package/types/storages/types.d.ts +6 -5
- package/types/sync/polling/fetchers/types.d.ts +1 -1
- package/types/sync/polling/types.d.ts +9 -7
- package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +1 -1
- package/types/sync/streaming/SSEHandler/types.d.ts +6 -6
- package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +3 -2
- package/types/sync/streaming/constants.d.ts +2 -2
- package/types/sync/streaming/parseUtils.d.ts +2 -2
- package/types/sync/streaming/types.d.ts +5 -5
- package/types/sync/submitters/types.d.ts +4 -5
- package/types/utils/constants/index.d.ts +1 -2
|
@@ -2,81 +2,130 @@ 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 {
|
|
5
|
+
import { MEMBERSHIPS } from '../../../utils/constants';
|
|
6
|
+
import { ISegmentsCacheSync, IStorageSync } from '../../../storages/types';
|
|
7
|
+
import { ILogger } from '../../../logger/types';
|
|
8
|
+
import { FETCH_BACKOFF_MAX_RETRIES } from './constants';
|
|
9
|
+
import { MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../constants';
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* MySegmentsUpdateWorker factory
|
|
9
13
|
*/
|
|
10
|
-
export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask, telemetryTracker: ITelemetryTracker
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
14
|
+
export function MySegmentsUpdateWorker(log: ILogger, storage: Pick<IStorageSync, 'segments' | 'largeSegments'>, mySegmentsSyncTask: IMySegmentsSyncTask, telemetryTracker: ITelemetryTracker): IUpdateWorker<[mySegmentsData?: Pick<MySegmentsData, 'type' | 'cn'>, payload?: Pick<MySegmentsData, 'added' | 'removed'>, delay?: number]> {
|
|
15
|
+
|
|
16
|
+
function createUpdateWorker(mySegmentsCache: ISegmentsCacheSync) {
|
|
17
|
+
|
|
18
|
+
let maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
|
|
19
|
+
let currentChangeNumber = -1;
|
|
20
|
+
let handleNewEvent = false;
|
|
21
|
+
let isHandlingEvent: boolean;
|
|
22
|
+
let cdnBypass: boolean;
|
|
23
|
+
let _segmentsData: MySegmentsData | undefined; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
|
|
24
|
+
let _delay: undefined | number;
|
|
25
|
+
let _delayTimeoutID: any;
|
|
26
|
+
const backoff = new Backoff(__handleMySegmentsUpdateCall);
|
|
27
|
+
|
|
28
|
+
function __handleMySegmentsUpdateCall() {
|
|
29
|
+
isHandlingEvent = true;
|
|
30
|
+
if (maxChangeNumber > Math.max(currentChangeNumber, mySegmentsCache.getChangeNumber())) {
|
|
31
|
+
handleNewEvent = false;
|
|
32
|
+
const currentMaxChangeNumber = maxChangeNumber;
|
|
33
|
+
|
|
34
|
+
// fetch mySegments revalidating data if cached
|
|
35
|
+
const syncTask = _delay ?
|
|
36
|
+
new Promise(res => {
|
|
37
|
+
_delayTimeoutID = setTimeout(() => {
|
|
38
|
+
_delay = undefined;
|
|
39
|
+
mySegmentsSyncTask.execute(_segmentsData, true, cdnBypass ? maxChangeNumber : undefined).then(res);
|
|
40
|
+
}, _delay);
|
|
41
|
+
}) :
|
|
42
|
+
mySegmentsSyncTask.execute(_segmentsData, true, cdnBypass ? maxChangeNumber : undefined);
|
|
43
|
+
|
|
44
|
+
syncTask.then((result) => {
|
|
45
|
+
if (!isHandlingEvent) return; // halt if `stop` has been called
|
|
46
|
+
if (result !== false) { // Unlike `Splits|SegmentsUpdateWorker`, `mySegmentsCache.getChangeNumber` can be -1, since `/memberships` change number is optional
|
|
47
|
+
const storageChangeNumber = mySegmentsCache.getChangeNumber();
|
|
48
|
+
currentChangeNumber = storageChangeNumber > -1 ?
|
|
49
|
+
storageChangeNumber :
|
|
50
|
+
Math.max(currentChangeNumber, currentMaxChangeNumber); // use `currentMaxChangeNumber`, in case that `maxChangeNumber` was updated during fetch.
|
|
51
|
+
}
|
|
52
|
+
if (handleNewEvent) {
|
|
53
|
+
__handleMySegmentsUpdateCall();
|
|
54
|
+
} else {
|
|
55
|
+
if (_segmentsData) telemetryTracker.trackUpdatesFromSSE(MEMBERSHIPS);
|
|
56
|
+
|
|
57
|
+
const attempts = backoff.attempts + 1;
|
|
58
|
+
|
|
59
|
+
if (maxChangeNumber <= currentChangeNumber) {
|
|
60
|
+
log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
|
|
61
|
+
isHandlingEvent = false;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
|
|
66
|
+
backoff.scheduleCall();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (cdnBypass) {
|
|
71
|
+
log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
|
|
72
|
+
isHandlingEvent = false;
|
|
73
|
+
} else {
|
|
74
|
+
backoff.reset();
|
|
75
|
+
cdnBypass = true;
|
|
76
|
+
__handleMySegmentsUpdateCall();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
} else {
|
|
81
|
+
isHandlingEvent = false;
|
|
82
|
+
}
|
|
51
83
|
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
/**
|
|
87
|
+
* Invoked by NotificationProcessor on MY_(LARGE)_SEGMENTS_UPDATE notifications
|
|
88
|
+
*
|
|
89
|
+
* @param changeNumber change number of the notification
|
|
90
|
+
* @param segmentsData data for KeyList or SegmentRemoval instant updates
|
|
91
|
+
* @param delay optional time to wait for BoundedFetchRequest or BoundedFetchRequest updates
|
|
92
|
+
*/
|
|
93
|
+
put(mySegmentsData: Pick<MySegmentsData, 'type' | 'cn'>, payload?: Pick<MySegmentsData, 'added' | 'removed'>, delay?: number) {
|
|
94
|
+
const { type, cn } = mySegmentsData;
|
|
95
|
+
// Ignore event if it is outdated or if there is a pending fetch request (_delay is set)
|
|
96
|
+
if (cn <= Math.max(currentChangeNumber, mySegmentsCache.getChangeNumber()) || cn <= maxChangeNumber || _delay) return;
|
|
97
|
+
|
|
98
|
+
maxChangeNumber = cn;
|
|
99
|
+
handleNewEvent = true;
|
|
100
|
+
cdnBypass = false;
|
|
101
|
+
_segmentsData = payload && { type, cn, added: payload.added, removed: payload.removed };
|
|
102
|
+
_delay = delay;
|
|
103
|
+
|
|
104
|
+
if (backoff.timeoutID || !isHandlingEvent) __handleMySegmentsUpdateCall();
|
|
105
|
+
backoff.reset();
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
stop() {
|
|
109
|
+
clearTimeout(_delayTimeoutID);
|
|
110
|
+
_delay = undefined;
|
|
111
|
+
isHandlingEvent = false;
|
|
112
|
+
backoff.reset();
|
|
113
|
+
}
|
|
114
|
+
};
|
|
52
115
|
}
|
|
53
116
|
|
|
117
|
+
const updateWorkers = {
|
|
118
|
+
[MEMBERSHIPS_MS_UPDATE]: createUpdateWorker(storage.segments),
|
|
119
|
+
[MEMBERSHIPS_LS_UPDATE]: createUpdateWorker(storage.largeSegments!),
|
|
120
|
+
};
|
|
121
|
+
|
|
54
122
|
return {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
*
|
|
58
|
-
* @param changeNumber change number of the notification
|
|
59
|
-
* @param segmentsData data for KeyList or SegmentRemoval instant updates
|
|
60
|
-
* @param delay optional time to wait for BoundedFetchRequest or BoundedFetchRequest updates
|
|
61
|
-
*/
|
|
62
|
-
put(changeNumber: number, segmentsData?: MySegmentsData, delay?: number) {
|
|
63
|
-
// 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
|
-
|
|
66
|
-
maxChangeNumber = changeNumber;
|
|
67
|
-
handleNewEvent = true;
|
|
68
|
-
_segmentsData = segmentsData;
|
|
69
|
-
_delay = delay;
|
|
70
|
-
|
|
71
|
-
if (backoff.timeoutID || !isHandlingEvent) __handleMySegmentsUpdateCall();
|
|
72
|
-
backoff.reset();
|
|
123
|
+
put(mySegmentsData: Pick<MySegmentsData, 'type' | 'cn'>, payload?: Pick<MySegmentsData, 'added' | 'removed'>, delay?: number) {
|
|
124
|
+
updateWorkers[mySegmentsData.type].put(mySegmentsData, payload, delay);
|
|
73
125
|
},
|
|
74
|
-
|
|
75
126
|
stop() {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
isHandlingEvent = false;
|
|
79
|
-
backoff.reset();
|
|
127
|
+
updateWorkers[MEMBERSHIPS_MS_UPDATE].stop();
|
|
128
|
+
updateWorkers[MEMBERSHIPS_LS_UPDATE].stop();
|
|
80
129
|
}
|
|
81
130
|
};
|
|
82
131
|
}
|
|
@@ -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
|
|
28
|
+
export const MEMBERSHIPS_MS_UPDATE = 'MEMBERSHIPS_MS_UPDATE';
|
|
29
|
+
export const MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_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,
|
|
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<
|
|
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;
|
|
@@ -11,14 +11,14 @@ 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 {
|
|
15
|
-
import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT,
|
|
16
|
-
import {
|
|
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
|
+
import { IMembershipMSUpdateData, IMembershipLSUpdateData, KeyList, UpdateStrategy } from './SSEHandler/types';
|
|
17
17
|
import { getDelay, isInBitmap, parseBitmap, parseFFUpdatePayload, parseKeyList } from './parseUtils';
|
|
18
18
|
import { ISet, _Set } from '../../utils/lang/sets';
|
|
19
19
|
import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64';
|
|
20
20
|
import { IAuthTokenPushEnabled } from './AuthClient/types';
|
|
21
|
-
import { TOKEN_REFRESH, AUTH_REJECTION
|
|
21
|
+
import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants';
|
|
22
22
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -59,11 +59,11 @@ export function pushManagerFactory(
|
|
|
59
59
|
// For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
|
|
60
60
|
const splitsUpdateWorker = SplitsUpdateWorker(log, storage.splits, pollingManager.splitsSyncTask, readiness.splits, telemetryTracker, userKey ? undefined : pollingManager.segmentsSyncTask as ISegmentsSyncTask);
|
|
61
61
|
|
|
62
|
-
// [Only for client-side] map of hashes to user keys, to dispatch
|
|
62
|
+
// [Only for client-side] map of hashes to user keys, to dispatch membership update events to the corresponding MySegmentsUpdateWorker
|
|
63
63
|
const userKeyHashes: Record<string, string> = {};
|
|
64
64
|
// [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
|
|
65
|
-
// Hash64 is used to process
|
|
66
|
-
const clients: Record<string, { hash64: Hash64, worker: ReturnType<typeof MySegmentsUpdateWorker
|
|
65
|
+
// Hash64 is used to process membership update events and dispatch actions to the corresponding MySegmentsUpdateWorker.
|
|
66
|
+
const clients: Record<string, { hash64: Hash64, worker: ReturnType<typeof MySegmentsUpdateWorker> }> = {};
|
|
67
67
|
|
|
68
68
|
// [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
|
|
69
69
|
let connectForNewClient = false;
|
|
@@ -170,10 +170,7 @@ export function pushManagerFactory(
|
|
|
170
170
|
// cancel scheduled fetch retries of Splits, Segments, and MySegments Update Workers
|
|
171
171
|
function stopWorkers() {
|
|
172
172
|
splitsUpdateWorker.stop();
|
|
173
|
-
if (userKey) forOwn(clients, ({ worker
|
|
174
|
-
worker.stop();
|
|
175
|
-
workerLarge.stop();
|
|
176
|
-
});
|
|
173
|
+
if (userKey) forOwn(clients, ({ worker }) => worker.stop());
|
|
177
174
|
else segmentsUpdateWorker!.stop();
|
|
178
175
|
}
|
|
179
176
|
|
|
@@ -238,22 +235,20 @@ export function pushManagerFactory(
|
|
|
238
235
|
splitsUpdateWorker.put(parsedData);
|
|
239
236
|
});
|
|
240
237
|
|
|
241
|
-
function handleMySegmentsUpdate(parsedData:
|
|
242
|
-
const isLS = parsedData.t === MY_LARGE_SEGMENTS_UPDATE;
|
|
243
|
-
|
|
238
|
+
function handleMySegmentsUpdate(parsedData: IMembershipMSUpdateData | IMembershipLSUpdateData) {
|
|
244
239
|
switch (parsedData.u) {
|
|
245
240
|
case UpdateStrategy.BoundedFetchRequest: {
|
|
246
241
|
let bitmap: Uint8Array;
|
|
247
242
|
try {
|
|
248
243
|
bitmap = parseBitmap(parsedData.d!, parsedData.c!);
|
|
249
244
|
} catch (e) {
|
|
250
|
-
log.warn(
|
|
245
|
+
log.warn(STREAMING_PARSING_MEMBERSHIPS_UPDATE, ['BoundedFetchRequest', e]);
|
|
251
246
|
break;
|
|
252
247
|
}
|
|
253
248
|
|
|
254
|
-
forOwn(clients, ({ hash64, worker
|
|
249
|
+
forOwn(clients, ({ hash64, worker }, matchingKey) => {
|
|
255
250
|
if (isInBitmap(bitmap, hash64.hex)) {
|
|
256
|
-
|
|
251
|
+
worker.put(parsedData, undefined, getDelay(parsedData, matchingKey));
|
|
257
252
|
}
|
|
258
253
|
});
|
|
259
254
|
return;
|
|
@@ -265,53 +260,50 @@ export function pushManagerFactory(
|
|
|
265
260
|
added = new _Set(keyList.a);
|
|
266
261
|
removed = new _Set(keyList.r);
|
|
267
262
|
} catch (e) {
|
|
268
|
-
log.warn(
|
|
263
|
+
log.warn(STREAMING_PARSING_MEMBERSHIPS_UPDATE, ['KeyList', e]);
|
|
269
264
|
break;
|
|
270
265
|
}
|
|
271
266
|
|
|
272
267
|
if (!parsedData.n || !parsedData.n.length) {
|
|
273
|
-
log.warn(
|
|
268
|
+
log.warn(STREAMING_PARSING_MEMBERSHIPS_UPDATE, ['KeyList', 'No segment name was provided']);
|
|
274
269
|
break;
|
|
275
270
|
}
|
|
276
271
|
|
|
277
|
-
forOwn(clients, ({ hash64, worker
|
|
272
|
+
forOwn(clients, ({ hash64, worker }) => {
|
|
278
273
|
const add = added.has(hash64.dec) ? true : removed.has(hash64.dec) ? false : undefined;
|
|
279
274
|
if (add !== undefined) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}]);
|
|
275
|
+
worker.put(parsedData, {
|
|
276
|
+
added: add ? [parsedData.n![0]] : [],
|
|
277
|
+
removed: add ? [] : [parsedData.n![0]]
|
|
278
|
+
});
|
|
285
279
|
}
|
|
286
280
|
});
|
|
287
281
|
return;
|
|
288
282
|
}
|
|
289
283
|
case UpdateStrategy.SegmentRemoval:
|
|
290
284
|
if (!parsedData.n || !parsedData.n.length) {
|
|
291
|
-
log.warn(
|
|
285
|
+
log.warn(STREAMING_PARSING_MEMBERSHIPS_UPDATE, ['SegmentRemoval', 'No segment name was provided']);
|
|
292
286
|
break;
|
|
293
287
|
}
|
|
294
288
|
|
|
295
|
-
forOwn(clients, ({ worker
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
cn: parsedData.cn
|
|
301
|
-
})));
|
|
289
|
+
forOwn(clients, ({ worker }) => {
|
|
290
|
+
worker.put(parsedData, {
|
|
291
|
+
added: [],
|
|
292
|
+
removed: parsedData.n!
|
|
293
|
+
});
|
|
302
294
|
});
|
|
303
295
|
return;
|
|
304
296
|
}
|
|
305
297
|
|
|
306
298
|
// `UpdateStrategy.UnboundedFetchRequest` and fallbacks of other cases
|
|
307
|
-
forOwn(clients, ({ worker
|
|
308
|
-
|
|
299
|
+
forOwn(clients, ({ worker }, matchingKey) => {
|
|
300
|
+
worker.put(parsedData, undefined, getDelay(parsedData, matchingKey));
|
|
309
301
|
});
|
|
310
302
|
}
|
|
311
303
|
|
|
312
304
|
if (userKey) {
|
|
313
|
-
pushEmitter.on(
|
|
314
|
-
pushEmitter.on(
|
|
305
|
+
pushEmitter.on(MEMBERSHIPS_MS_UPDATE, handleMySegmentsUpdate);
|
|
306
|
+
pushEmitter.on(MEMBERSHIPS_LS_UPDATE, handleMySegmentsUpdate);
|
|
315
307
|
} else {
|
|
316
308
|
pushEmitter.on(SEGMENT_UPDATE, segmentsUpdateWorker!.put);
|
|
317
309
|
}
|
|
@@ -351,8 +343,7 @@ export function pushManagerFactory(
|
|
|
351
343
|
userKeyHashes[hash] = userKey;
|
|
352
344
|
clients[userKey] = {
|
|
353
345
|
hash64: hash64(userKey),
|
|
354
|
-
worker: MySegmentsUpdateWorker(mySegmentsSyncTask, telemetryTracker
|
|
355
|
-
workerLarge: MySegmentsUpdateWorker(mySegmentsSyncTask, telemetryTracker, MY_LARGE_SEGMENT)
|
|
346
|
+
worker: MySegmentsUpdateWorker(log, storage, mySegmentsSyncTask, telemetryTracker)
|
|
356
347
|
};
|
|
357
348
|
connectForNewClient = true; // we must reconnect on start, to listen the channel for the new user key
|
|
358
349
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IMembershipMSUpdateData, IMembershipLSUpdateData, ISegmentUpdateData, ISplitUpdateData, ISplitKillData, INotificationData } from './SSEHandler/types';
|
|
2
2
|
import { ITask } from '../types';
|
|
3
3
|
import { IMySegmentsSyncTask } from '../polling/types';
|
|
4
4
|
import { IEventEmitter } from '../../types';
|
|
@@ -11,21 +11,21 @@ export type PUSH_NONRETRYABLE_ERROR = 'PUSH_NONRETRYABLE_ERROR'
|
|
|
11
11
|
export type PUSH_RETRYABLE_ERROR = 'PUSH_RETRYABLE_ERROR'
|
|
12
12
|
|
|
13
13
|
// Update-type push notifications, handled by NotificationProcessor
|
|
14
|
-
export type
|
|
14
|
+
export type MEMBERSHIPS_MS_UPDATE = 'MEMBERSHIPS_MS_UPDATE';
|
|
15
|
+
export type MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
|
|
15
16
|
export type SEGMENT_UPDATE = 'SEGMENT_UPDATE';
|
|
16
17
|
export type SPLIT_KILL = 'SPLIT_KILL';
|
|
17
18
|
export type SPLIT_UPDATE = 'SPLIT_UPDATE';
|
|
18
|
-
export type MY_LARGE_SEGMENTS_UPDATE = 'MY_LARGE_SEGMENTS_UPDATE';
|
|
19
19
|
|
|
20
20
|
// Control-type push notifications, handled by NotificationKeeper
|
|
21
21
|
export type CONTROL = 'CONTROL';
|
|
22
22
|
export type OCCUPANCY = 'OCCUPANCY';
|
|
23
23
|
|
|
24
|
-
export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR |
|
|
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
|
|
25
25
|
|
|
26
26
|
type IParsedData<T extends IPushEvent> =
|
|
27
|
-
T extends
|
|
28
|
-
T extends
|
|
27
|
+
T extends MEMBERSHIPS_MS_UPDATE ? IMembershipMSUpdateData :
|
|
28
|
+
T extends MEMBERSHIPS_LS_UPDATE ? IMembershipLSUpdateData :
|
|
29
29
|
T extends SEGMENT_UPDATE ? ISegmentUpdateData :
|
|
30
30
|
T extends SPLIT_UPDATE ? ISplitUpdateData :
|
|
31
31
|
T extends SPLIT_KILL ? ISplitKillData : INotificationData;
|
|
@@ -103,7 +103,7 @@ export type DROPPED = 1;
|
|
|
103
103
|
export type DEDUPED = 2;
|
|
104
104
|
export type ImpressionDataType = QUEUED | DROPPED | DEDUPED
|
|
105
105
|
export type EventDataType = QUEUED | DROPPED;
|
|
106
|
-
export type UpdatesFromSSEEnum = SPLITS |
|
|
106
|
+
export type UpdatesFromSSEEnum = SPLITS | MEMBERSHIPS;
|
|
107
107
|
|
|
108
108
|
export type SPLITS = 'sp';
|
|
109
109
|
export type IMPRESSIONS = 'im';
|
|
@@ -112,9 +112,8 @@ export type EVENTS = 'ev';
|
|
|
112
112
|
export type TELEMETRY = 'te';
|
|
113
113
|
export type TOKEN = 'to';
|
|
114
114
|
export type SEGMENT = 'se';
|
|
115
|
-
export type
|
|
116
|
-
export type
|
|
117
|
-
export type OperationType = SPLITS | IMPRESSIONS | IMPRESSIONS_COUNT | EVENTS | TELEMETRY | TOKEN | SEGMENT | MY_SEGMENT;
|
|
115
|
+
export type MEMBERSHIPS = 'ms';
|
|
116
|
+
export type OperationType = SPLITS | IMPRESSIONS | IMPRESSIONS_COUNT | EVENTS | TELEMETRY | TOKEN | SEGMENT | MEMBERSHIPS;
|
|
118
117
|
|
|
119
118
|
export type LastSync = Partial<Record<OperationType, number | undefined>>
|
|
120
119
|
export type HttpErrors = Partial<Record<OperationType, { [statusCode: string]: number }>>
|
|
@@ -177,7 +176,7 @@ export type TelemetryUsageStatsPayload = TelemetryUsageStats & {
|
|
|
177
176
|
spC?: number, // splitCount
|
|
178
177
|
seC?: number, // segmentCount
|
|
179
178
|
skC?: number, // segmentKeyCount
|
|
180
|
-
|
|
179
|
+
lsC?: number, // largeSegmentCount
|
|
181
180
|
lskC?: number, // largeSegmentKeyCount
|
|
182
181
|
sL?: number, // sessionLengthMs
|
|
183
182
|
eQ: number, // eventsQueued
|
|
@@ -75,8 +75,7 @@ export const EVENTS = 'ev';
|
|
|
75
75
|
export const TELEMETRY = 'te';
|
|
76
76
|
export const TOKEN = 'to';
|
|
77
77
|
export const SEGMENT = 'se';
|
|
78
|
-
export const
|
|
79
|
-
export const MY_LARGE_SEGMENT = 'mls';
|
|
78
|
+
export const MEMBERSHIPS = 'ms';
|
|
80
79
|
|
|
81
80
|
export const TREATMENT = 't';
|
|
82
81
|
export const TREATMENTS = 'ts';
|
package/types/dtos/types.d.ts
CHANGED
|
@@ -177,20 +177,16 @@ export interface ISegmentChangesResponse {
|
|
|
177
177
|
since: number;
|
|
178
178
|
till: number;
|
|
179
179
|
}
|
|
180
|
+
export interface IMySegmentsResponse {
|
|
181
|
+
cn?: number;
|
|
182
|
+
k?: {
|
|
183
|
+
n: string;
|
|
184
|
+
}[];
|
|
185
|
+
}
|
|
180
186
|
/** Interface of the parsed JSON response of `/memberships/{userKey}` */
|
|
181
187
|
export interface IMembershipsResponse {
|
|
182
|
-
ms?:
|
|
183
|
-
|
|
184
|
-
k?: Array<{
|
|
185
|
-
n: string;
|
|
186
|
-
}>;
|
|
187
|
-
};
|
|
188
|
-
ls?: {
|
|
189
|
-
cn?: number;
|
|
190
|
-
k?: Array<{
|
|
191
|
-
n: string;
|
|
192
|
-
}>;
|
|
193
|
-
};
|
|
188
|
+
ms?: IMySegmentsResponse;
|
|
189
|
+
ls?: IMySegmentsResponse;
|
|
194
190
|
}
|
|
195
191
|
/** Metadata internal type for storages */
|
|
196
192
|
export interface IMetadata {
|
|
@@ -77,7 +77,7 @@ export declare const WARN_SPLITS_FILTER_IGNORED = 219;
|
|
|
77
77
|
export declare const WARN_SPLITS_FILTER_INVALID = 220;
|
|
78
78
|
export declare const WARN_SPLITS_FILTER_EMPTY = 221;
|
|
79
79
|
export declare const WARN_SDK_KEY = 222;
|
|
80
|
-
export declare const
|
|
80
|
+
export declare const STREAMING_PARSING_MEMBERSHIPS_UPDATE = 223;
|
|
81
81
|
export declare const STREAMING_PARSING_SPLIT_UPDATE = 224;
|
|
82
82
|
export declare const WARN_INVALID_FLAGSET = 225;
|
|
83
83
|
export declare const WARN_LOWERCASE_FLAGSET = 226;
|
|
@@ -18,7 +18,7 @@ export declare type ISplitHttpClient = (url: string, options?: IRequestOptions,
|
|
|
18
18
|
export declare type IFetchAuth = (userKeys?: string[]) => Promise<IResponse>;
|
|
19
19
|
export declare type IFetchSplitChanges = (since: number, noCache?: boolean, till?: number) => Promise<IResponse>;
|
|
20
20
|
export declare type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean, till?: number) => Promise<IResponse>;
|
|
21
|
-
export declare type IFetchMemberships = (userMatchingKey: string, noCache?: boolean) => Promise<IResponse>;
|
|
21
|
+
export declare type IFetchMemberships = (userMatchingKey: string, noCache?: boolean, till?: number) => Promise<IResponse>;
|
|
22
22
|
export declare type IPostEventsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
|
|
23
23
|
export declare type IPostUniqueKeysBulkCs = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
|
|
24
24
|
export declare type IPostUniqueKeysBulkSs = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { IMySegmentsResponse } from '../dtos/types';
|
|
2
|
+
import { MySegmentsData } from '../sync/polling/types';
|
|
1
3
|
import { ISegmentsCacheSync } from './types';
|
|
2
4
|
/**
|
|
3
5
|
* This class provides a skeletal implementation of the ISegmentsCacheSync interface
|
|
@@ -22,7 +24,7 @@ export declare abstract class AbstractSegmentsCacheSync implements ISegmentsCach
|
|
|
22
24
|
/**
|
|
23
25
|
* clear the cache.
|
|
24
26
|
*/
|
|
25
|
-
|
|
27
|
+
clear(): void;
|
|
26
28
|
/**
|
|
27
29
|
* 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.
|
|
28
30
|
* For client-side synchronizer: the method is not used.
|
|
@@ -39,14 +41,14 @@ export declare abstract class AbstractSegmentsCacheSync implements ISegmentsCach
|
|
|
39
41
|
*/
|
|
40
42
|
abstract getKeysCount(): number;
|
|
41
43
|
/**
|
|
42
|
-
* For server-side synchronizer:
|
|
43
|
-
* For client-side synchronizer:
|
|
44
|
+
* For server-side synchronizer: change number of `name` segment.
|
|
45
|
+
* For client-side synchronizer: change number of mySegments.
|
|
44
46
|
*/
|
|
45
|
-
setChangeNumber(name
|
|
47
|
+
abstract setChangeNumber(name?: string, changeNumber?: number): boolean | void;
|
|
46
48
|
abstract getChangeNumber(name: string): number;
|
|
47
49
|
/**
|
|
48
50
|
* For server-side synchronizer: the method is not used.
|
|
49
|
-
* For client-side synchronizer:
|
|
51
|
+
* For client-side synchronizer: it resets or updates the cache.
|
|
50
52
|
*/
|
|
51
|
-
resetSegments(
|
|
53
|
+
resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean;
|
|
52
54
|
}
|
|
@@ -12,7 +12,7 @@ export declare abstract class AbstractSplitsCacheSync implements ISplitsCacheSyn
|
|
|
12
12
|
removeSplits(names: string[]): boolean[];
|
|
13
13
|
abstract getSplit(name: string): ISplit | null;
|
|
14
14
|
getSplits(names: string[]): Record<string, ISplit | null>;
|
|
15
|
-
abstract setChangeNumber(changeNumber: number): boolean;
|
|
15
|
+
abstract setChangeNumber(changeNumber: number): boolean | void;
|
|
16
16
|
abstract getChangeNumber(): number;
|
|
17
17
|
getAll(): ISplit[];
|
|
18
18
|
abstract getSplitNames(): string[];
|
|
@@ -5,22 +5,11 @@ export declare class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
|
|
|
5
5
|
private readonly keys;
|
|
6
6
|
private readonly log;
|
|
7
7
|
constructor(log: ILogger, keys: MySegmentsKeyBuilder);
|
|
8
|
-
/**
|
|
9
|
-
* Removes list of segments from localStorage
|
|
10
|
-
* @NOTE this method is not being used at the moment.
|
|
11
|
-
*/
|
|
12
|
-
clear(): void;
|
|
13
8
|
addToSegment(name: string): boolean;
|
|
14
9
|
removeFromSegment(name: string): boolean;
|
|
15
10
|
isInSegment(name: string): boolean;
|
|
16
|
-
/**
|
|
17
|
-
* Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
|
|
18
|
-
*
|
|
19
|
-
* @param {string[]} names list of segment names
|
|
20
|
-
* @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
|
|
21
|
-
*/
|
|
22
|
-
resetSegments(names: string[], changeNumber?: number): boolean;
|
|
23
11
|
getRegisteredSegments(): string[];
|
|
24
12
|
getKeysCount(): number;
|
|
13
|
+
setChangeNumber(name?: string, changeNumber?: number): void;
|
|
25
14
|
getChangeNumber(): number;
|
|
26
15
|
}
|
|
@@ -6,18 +6,10 @@ import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
|
|
|
6
6
|
export declare class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
|
|
7
7
|
private segmentCache;
|
|
8
8
|
private cn?;
|
|
9
|
-
clear(): void;
|
|
10
9
|
addToSegment(name: string): boolean;
|
|
11
10
|
removeFromSegment(name: string): boolean;
|
|
12
11
|
isInSegment(name: string): boolean;
|
|
13
|
-
|
|
14
|
-
* Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
|
|
15
|
-
* @NOTE based on the way we use segments in the browser, this way is the best option
|
|
16
|
-
*
|
|
17
|
-
* @param {string[]} names list of segment names
|
|
18
|
-
* @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
|
|
19
|
-
*/
|
|
20
|
-
resetSegments(names: string[], changeNumber?: number): boolean;
|
|
12
|
+
setChangeNumber(name?: string, changeNumber?: number): void;
|
|
21
13
|
getChangeNumber(): number;
|
|
22
14
|
getRegisteredSegments(): string[];
|
|
23
15
|
getKeysCount(): number;
|
|
@@ -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';
|
|
@@ -200,7 +201,7 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
200
201
|
removeSplits(names: string[]): boolean[];
|
|
201
202
|
getSplit(name: string): ISplit | null;
|
|
202
203
|
getSplits(names: string[]): Record<string, ISplit | null>;
|
|
203
|
-
setChangeNumber(changeNumber: number): boolean;
|
|
204
|
+
setChangeNumber(changeNumber: number): boolean | void;
|
|
204
205
|
getChangeNumber(): number;
|
|
205
206
|
getAll(): ISplit[];
|
|
206
207
|
getSplitNames(): string[];
|
|
@@ -245,9 +246,9 @@ export interface ISegmentsCacheSync extends ISegmentsCacheBase {
|
|
|
245
246
|
registerSegments(names: string[]): boolean;
|
|
246
247
|
getRegisteredSegments(): string[];
|
|
247
248
|
getKeysCount(): number;
|
|
248
|
-
setChangeNumber(name: string, changeNumber: number): boolean;
|
|
249
|
-
getChangeNumber(name
|
|
250
|
-
resetSegments(
|
|
249
|
+
setChangeNumber(name: string, changeNumber: number): boolean | void;
|
|
250
|
+
getChangeNumber(name?: string): number;
|
|
251
|
+
resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean;
|
|
251
252
|
clear(): void;
|
|
252
253
|
}
|
|
253
254
|
export interface ISegmentsCacheAsync extends ISegmentsCacheBase {
|
|
@@ -2,4 +2,4 @@ import { ISplitChangesResponse, ISegmentChangesResponse, IMembershipsResponse }
|
|
|
2
2
|
import { IResponse } from '../../../services/types';
|
|
3
3
|
export declare type ISplitChangesFetcher = (since: number, noCache?: boolean, till?: number, decorator?: (promise: Promise<IResponse>) => Promise<IResponse>) => Promise<ISplitChangesResponse>;
|
|
4
4
|
export declare type ISegmentChangesFetcher = (since: number, segmentName: string, noCache?: boolean, till?: number, decorator?: (promise: Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>;
|
|
5
|
-
export declare type IMySegmentsFetcher = (userMatchingKey: string, noCache?: boolean, decorator?: (promise: Promise<IResponse>) => Promise<IResponse>) => Promise<IMembershipsResponse>;
|
|
5
|
+
export declare type IMySegmentsFetcher = (userMatchingKey: string, noCache?: boolean, till?: number, decorator?: (promise: Promise<IResponse>) => Promise<IResponse>) => Promise<IMembershipsResponse>;
|