@splitsoftware/splitio-commons 0.1.1-canary.9 → 0.1.1-rc.18
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/evaluator/matchers/matcherTypes.js +4 -4
- package/cjs/evaluator/matchersTransform/index.js +11 -11
- package/cjs/evaluator/value/sanitize.js +6 -6
- package/cjs/listeners/browser.js +1 -2
- package/cjs/listeners/node.js +0 -3
- package/cjs/logger/constants.js +3 -1
- package/cjs/logger/messages/error.js +3 -2
- package/cjs/logger/messages/info.js +2 -2
- package/cjs/logger/messages/warn.js +2 -1
- package/cjs/readiness/readinessManager.js +10 -7
- package/cjs/sdkFactory/index.js +1 -4
- package/cjs/services/splitApi.js +1 -1
- package/cjs/services/splitHttpClient.js +5 -4
- package/cjs/storages/AbstractSplitsCacheSync.js +1 -1
- package/cjs/storages/inLocalStorage/index.js +5 -2
- package/cjs/storages/inMemory/InMemoryStorage.js +2 -0
- package/cjs/storages/inMemory/InMemoryStorageCS.js +2 -0
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +6 -2
- package/cjs/storages/inRedis/index.js +5 -2
- package/cjs/storages/pluggable/SplitsCachePluggable.js +6 -2
- package/cjs/storages/pluggable/inMemoryWrapper.js +6 -7
- package/cjs/storages/pluggable/index.js +5 -2
- package/cjs/storages/pluggable/wrapperAdapter.js +0 -1
- package/cjs/sync/offline/splitsParser/splitsParserFromFile.js +92 -89
- package/cjs/sync/offline/splitsParser/splitsParserFromSettings.js +45 -42
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +14 -4
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +30 -10
- package/cjs/sync/streaming/SSEClient/index.js +0 -11
- package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +7 -0
- package/cjs/sync/streaming/SSEHandler/NotificationParser.js +4 -1
- package/cjs/sync/streaming/SSEHandler/index.js +8 -9
- package/cjs/sync/streaming/SSEHandler/types.js +14 -0
- package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +5 -5
- package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -1
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +5 -3
- package/cjs/sync/streaming/constants.js +3 -1
- package/cjs/sync/streaming/mySegmentsV2utils.js +75 -0
- package/cjs/sync/streaming/pushManager.js +141 -40
- package/cjs/sync/submitters/metricsSyncTask.js +1 -1
- package/cjs/sync/submitters/submitterSyncTask.js +2 -2
- package/cjs/sync/syncManagerFromFile.js +15 -0
- package/cjs/sync/syncManagerFromObject.js +14 -0
- package/cjs/sync/syncManagerOffline.js +3 -3
- package/cjs/sync/syncManagerOnline.js +5 -3
- package/cjs/trackers/impressionObserver/ImpressionObserver.js +0 -2
- package/cjs/trackers/impressionObserver/buildKey.js +3 -9
- package/cjs/trackers/impressionObserver/impressionObserverCS.js +2 -2
- package/cjs/trackers/impressionObserver/impressionObserverSS.js +3 -3
- package/cjs/utils/constants/index.js +4 -1
- package/cjs/utils/decompress/index.js +427 -0
- package/cjs/utils/murmur3/{commons.js → common.js} +2 -6
- package/cjs/utils/murmur3/murmur3.js +11 -12
- package/cjs/utils/murmur3/murmur3_128.js +7 -142
- package/cjs/utils/murmur3/murmur3_128_x86.js +154 -0
- package/cjs/utils/murmur3/murmur3_64.js +36 -0
- package/cjs/utils/murmur3/utfx.js +100 -106
- package/cjs/utils/promise/wrapper.js +14 -11
- package/cjs/utils/settingsValidation/index.js +5 -2
- package/cjs/utils/settingsValidation/localhost/index.js +20 -0
- package/cjs/utils/settingsValidation/splitFilters.js +0 -1
- package/cjs/utils/settingsValidation/storage/storageCS.js +18 -8
- package/cjs/utils/settingsValidation/url.js +1 -1
- package/esm/evaluator/matchers/matcherTypes.js +2 -2
- package/esm/evaluator/matchersTransform/index.js +12 -12
- package/esm/evaluator/value/sanitize.js +7 -7
- package/esm/listeners/browser.js +1 -2
- package/esm/listeners/node.js +0 -3
- package/esm/logger/constants.js +2 -0
- package/esm/logger/messages/error.js +3 -2
- package/esm/logger/messages/info.js +2 -2
- package/esm/logger/messages/warn.js +2 -1
- package/esm/readiness/readinessManager.js +10 -7
- package/esm/sdkFactory/index.js +1 -4
- package/esm/services/splitApi.js +1 -1
- package/esm/services/splitHttpClient.js +5 -4
- package/esm/storages/AbstractSplitsCacheSync.js +1 -1
- package/esm/storages/inLocalStorage/index.js +5 -2
- package/esm/storages/inMemory/InMemoryStorage.js +2 -0
- package/esm/storages/inMemory/InMemoryStorageCS.js +2 -0
- package/esm/storages/inRedis/SplitsCacheInRedis.js +6 -2
- package/esm/storages/inRedis/index.js +5 -2
- package/esm/storages/pluggable/SplitsCachePluggable.js +6 -2
- package/esm/storages/pluggable/inMemoryWrapper.js +6 -7
- package/esm/storages/pluggable/index.js +5 -2
- package/esm/storages/pluggable/wrapperAdapter.js +0 -1
- package/esm/sync/offline/splitsParser/splitsParserFromFile.js +90 -88
- package/esm/sync/offline/splitsParser/splitsParserFromSettings.js +43 -41
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +15 -5
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +30 -10
- package/esm/sync/streaming/SSEClient/index.js +0 -11
- package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +7 -0
- package/esm/sync/streaming/SSEHandler/NotificationParser.js +4 -1
- package/esm/sync/streaming/SSEHandler/index.js +9 -10
- package/esm/sync/streaming/SSEHandler/types.js +13 -1
- package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +5 -5
- package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -1
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +5 -3
- package/esm/sync/streaming/constants.js +2 -0
- package/esm/sync/streaming/mySegmentsV2utils.js +69 -0
- package/esm/sync/streaming/pushManager.js +143 -42
- package/esm/sync/submitters/metricsSyncTask.js +1 -1
- package/esm/sync/submitters/submitterSyncTask.js +2 -2
- package/esm/sync/syncManagerFromFile.js +11 -0
- package/esm/sync/syncManagerFromObject.js +10 -0
- package/esm/sync/syncManagerOffline.js +3 -3
- package/esm/sync/syncManagerOnline.js +5 -3
- package/esm/trackers/impressionObserver/ImpressionObserver.js +0 -2
- package/esm/trackers/impressionObserver/buildKey.js +2 -9
- package/esm/trackers/impressionObserver/impressionObserverCS.js +2 -2
- package/esm/trackers/impressionObserver/impressionObserverSS.js +3 -3
- package/esm/utils/constants/index.js +3 -0
- package/esm/utils/decompress/index.js +424 -0
- package/esm/utils/murmur3/{commons.js → common.js} +1 -4
- package/esm/utils/murmur3/murmur3.js +1 -2
- package/esm/utils/murmur3/murmur3_128.js +7 -142
- package/esm/utils/murmur3/murmur3_128_x86.js +150 -0
- package/esm/utils/murmur3/murmur3_64.js +32 -0
- package/esm/utils/murmur3/utfx.js +96 -106
- package/esm/utils/promise/wrapper.js +14 -11
- package/esm/utils/settingsValidation/index.js +5 -2
- package/esm/utils/settingsValidation/localhost/index.js +16 -0
- package/esm/utils/settingsValidation/splitFilters.js +0 -1
- package/esm/utils/settingsValidation/storage/storageCS.js +16 -7
- package/esm/utils/settingsValidation/url.js +1 -1
- package/package.json +5 -5
- package/src/evaluator/matchers/matcherTypes.ts +2 -2
- package/src/evaluator/matchersTransform/index.ts +12 -12
- package/src/evaluator/value/sanitize.ts +7 -7
- package/src/listeners/browser.ts +1 -1
- package/src/listeners/node.ts +1 -2
- package/src/logger/constants.ts +2 -0
- package/src/logger/messages/error.ts +3 -2
- package/src/logger/messages/info.ts +2 -2
- package/src/logger/messages/warn.ts +3 -1
- package/src/readiness/readinessManager.ts +9 -7
- package/src/sdkFactory/index.ts +1 -3
- package/src/sdkFactory/types.ts +3 -3
- package/src/services/splitApi.ts +2 -3
- package/src/services/splitHttpClient.ts +6 -5
- package/src/services/types.ts +5 -5
- package/src/storages/AbstractSplitsCacheSync.ts +1 -1
- package/src/storages/inLocalStorage/index.ts +8 -4
- package/src/storages/inMemory/InMemoryStorage.ts +3 -0
- package/src/storages/inMemory/InMemoryStorageCS.ts +3 -0
- package/src/storages/inRedis/SplitsCacheInRedis.ts +3 -1
- package/src/storages/inRedis/index.ts +8 -4
- package/src/storages/pluggable/SplitsCachePluggable.ts +3 -1
- package/src/storages/pluggable/inMemoryWrapper.ts +6 -7
- package/src/storages/pluggable/index.ts +8 -4
- package/src/storages/pluggable/wrapperAdapter.ts +0 -1
- package/src/storages/types.ts +18 -15
- package/src/sync/offline/splitsParser/splitsParserFromFile.ts +110 -105
- package/src/sync/offline/splitsParser/splitsParserFromSettings.ts +45 -41
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +15 -5
- package/src/sync/polling/types.ts +2 -1
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +28 -10
- package/src/sync/streaming/AuthClient/types.ts +3 -0
- package/src/sync/streaming/SSEClient/index.ts +1 -15
- package/src/sync/streaming/SSEClient/types.ts +0 -1
- package/src/sync/streaming/SSEHandler/NotificationKeeper.ts +8 -0
- package/src/sync/streaming/SSEHandler/NotificationParser.ts +4 -2
- package/src/sync/streaming/SSEHandler/index.ts +11 -20
- package/src/sync/streaming/SSEHandler/types.ts +37 -3
- package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +7 -6
- package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +2 -1
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +4 -3
- package/src/sync/streaming/UpdateWorkers/types.ts +1 -1
- package/src/sync/streaming/constants.ts +2 -0
- package/src/sync/streaming/mySegmentsV2utils.ts +77 -0
- package/src/sync/streaming/pushManager.ts +139 -42
- package/src/sync/streaming/types.ts +14 -22
- package/src/sync/submitters/metricsSyncTask.ts +1 -1
- package/src/sync/submitters/submitterSyncTask.ts +2 -1
- package/src/sync/syncManagerFromFile.ts +13 -0
- package/src/sync/syncManagerFromObject.ts +12 -0
- package/src/sync/syncManagerOffline.ts +3 -3
- package/src/sync/syncManagerOnline.ts +6 -3
- package/src/trackers/impressionObserver/ImpressionObserver.ts +4 -6
- package/src/trackers/impressionObserver/buildKey.ts +2 -16
- package/src/trackers/impressionObserver/impressionObserverCS.ts +2 -2
- package/src/trackers/impressionObserver/impressionObserverSS.ts +3 -3
- package/src/types.ts +16 -2
- package/src/utils/constants/index.ts +6 -1
- package/src/utils/decompress/index.ts +429 -0
- package/src/utils/murmur3/{commons.ts → common.ts} +1 -5
- package/src/utils/murmur3/murmur3.ts +5 -5
- package/src/utils/murmur3/murmur3_128.ts +7 -180
- package/src/utils/murmur3/murmur3_128_x86.ts +188 -0
- package/src/utils/murmur3/murmur3_64.ts +36 -0
- package/src/utils/murmur3/utfx.ts +92 -110
- package/src/utils/promise/wrapper.ts +12 -9
- package/src/utils/settingsValidation/index.ts +8 -4
- package/src/utils/settingsValidation/localhost/index.ts +19 -0
- package/src/utils/settingsValidation/splitFilters.ts +0 -1
- package/src/utils/settingsValidation/storage/storageCS.ts +21 -8
- package/src/utils/settingsValidation/types.ts +2 -11
- package/src/utils/settingsValidation/url.ts +1 -1
- package/types/evaluator/matchers/matcherTypes.d.ts +2 -2
- package/types/listeners/browser.d.ts +1 -0
- package/types/listeners/node.d.ts +0 -1
- package/types/logger/constants.d.ts +2 -0
- package/types/sdkFactory/types.d.ts +3 -3
- package/types/services/types.d.ts +1 -0
- package/types/storages/inLocalStorage/index.d.ts +2 -2
- package/types/storages/inMemory/InMemoryStorage.d.ts +3 -0
- package/types/storages/inMemory/InMemoryStorageCS.d.ts +3 -0
- package/types/storages/inRedis/index.d.ts +2 -2
- package/types/storages/pluggable/index.d.ts +2 -2
- package/types/storages/types.d.ts +15 -15
- package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +2 -7
- package/types/sync/offline/splitsParser/splitsParserFromSettings.d.ts +1 -5
- package/types/sync/polling/types.d.ts +2 -1
- package/types/sync/streaming/AuthClient/indexV1.d.ts +12 -0
- package/types/sync/streaming/AuthClient/indexV2.d.ts +8 -0
- package/types/sync/streaming/AuthClient/types.d.ts +2 -0
- package/types/sync/streaming/SSEClient/index.d.ts +1 -9
- package/types/sync/streaming/SSEClient/types.d.ts +0 -1
- package/types/sync/streaming/SSEHandler/NotificationParser.d.ts +3 -2
- package/types/sync/streaming/SSEHandler/types.d.ts +30 -2
- package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +4 -3
- package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
- package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
- package/types/sync/streaming/UpdateWorkers/types.d.ts +1 -1
- package/types/sync/streaming/constants.d.ts +3 -1
- package/types/sync/streaming/mySegmentsV2utils.d.ts +27 -0
- package/types/sync/streaming/pushManagerNoUsers.d.ts +13 -0
- package/types/sync/streaming/types.d.ts +9 -5
- package/types/sync/submitters/submitterSyncTask.d.ts +1 -1
- package/types/sync/syncManagerFromFile.d.ts +2 -0
- package/types/sync/syncManagerFromObject.d.ts +2 -0
- package/types/sync/syncManagerOffline.d.ts +1 -1
- package/types/trackers/impressionObserver/ImpressionObserver.d.ts +2 -2
- package/types/trackers/impressionObserver/buildKey.d.ts +1 -1
- package/types/trackers/impressionObserver/impressionObserverCS.d.ts +2 -2
- package/types/trackers/impressionObserver/impressionObserverSS.d.ts +2 -2
- package/types/types.d.ts +16 -2
- package/types/utils/constants/index.d.ts +5 -1
- package/types/utils/decompress/index.d.ts +16 -0
- package/types/utils/murmur3/common.d.ts +12 -0
- package/types/utils/murmur3/murmur3.d.ts +2 -2
- package/types/utils/murmur3/murmur3_128.d.ts +5 -0
- package/types/utils/murmur3/murmur3_128_x86.d.ts +7 -0
- package/types/utils/murmur3/murmur3_64.d.ts +10 -0
- package/types/utils/murmur3/utfx.d.ts +24 -6
- package/types/utils/settingsValidation/index.d.ts +3 -2
- package/types/utils/settingsValidation/localhost/index.d.ts +9 -0
- package/types/utils/settingsValidation/storage/storageCS.d.ts +7 -1
- package/types/utils/settingsValidation/types.d.ts +2 -10
- package/cjs/sync/streaming/pushManagerCS.js +0 -179
- package/cjs/sync/streaming/pushManagerSS.js +0 -128
- package/esm/sync/streaming/pushManagerCS.js +0 -175
- package/esm/sync/streaming/pushManagerSS.js +0 -124
- package/src/sync/streaming/pushManagerCS.ts +0 -238
- package/src/sync/streaming/pushManagerSS.ts +0 -177
|
@@ -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, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE } from '../constants';
|
|
3
|
+
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE } from '../constants';
|
|
4
4
|
import { IPushEventEmitter } from '../types';
|
|
5
5
|
import { ISseEventHandler } from '../SSEClient/types';
|
|
6
|
-
import { INotificationError } 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
|
|
|
@@ -43,7 +43,7 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
|
|
|
43
43
|
log.warn(STREAMING_PARSING_ERROR_FAILS, [err]);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
let errorMessage = errorWithParsedData.parsedData && errorWithParsedData.parsedData.message;
|
|
46
|
+
let errorMessage = (errorWithParsedData.parsedData && errorWithParsedData.parsedData.message) || errorWithParsedData.message;
|
|
47
47
|
log.error(ERROR_STREAMING_SSE, [errorMessage]);
|
|
48
48
|
|
|
49
49
|
if (isRetryableError(errorWithParsedData)) {
|
|
@@ -55,9 +55,10 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
|
|
|
55
55
|
|
|
56
56
|
/* NotificationProcessor */
|
|
57
57
|
handleMessage(message) {
|
|
58
|
-
let messageWithParsedData;
|
|
58
|
+
let messageWithParsedData: INotificationMessage | undefined;
|
|
59
59
|
try {
|
|
60
60
|
messageWithParsedData = messageParser(message);
|
|
61
|
+
if (!messageWithParsedData) return; // Messages with empty data are ignored
|
|
61
62
|
} catch (err) {
|
|
62
63
|
log.warn(STREAMING_PARSING_MESSAGE_FAILS, [err]);
|
|
63
64
|
return;
|
|
@@ -67,30 +68,19 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
|
|
|
67
68
|
log.debug(STREAMING_NEW_MESSAGE, [data]);
|
|
68
69
|
|
|
69
70
|
// we only handle update events if streaming is up.
|
|
70
|
-
if (!notificationKeeper.isStreamingUp() &&
|
|
71
|
+
if (!notificationKeeper.isStreamingUp() && [OCCUPANCY, CONTROL].indexOf(parsedData.type) === -1)
|
|
71
72
|
return;
|
|
72
73
|
|
|
73
74
|
switch (parsedData.type) {
|
|
74
75
|
/* update events */
|
|
75
76
|
case SPLIT_UPDATE:
|
|
76
|
-
pushEmitter.emit(SPLIT_UPDATE,
|
|
77
|
-
parsedData.changeNumber);
|
|
78
|
-
break;
|
|
79
77
|
case SEGMENT_UPDATE:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
case MY_SEGMENTS_UPDATE_V2:
|
|
79
|
+
case SPLIT_KILL:
|
|
80
|
+
pushEmitter.emit(parsedData.type, parsedData);
|
|
83
81
|
break;
|
|
84
82
|
case MY_SEGMENTS_UPDATE:
|
|
85
|
-
pushEmitter.emit(
|
|
86
|
-
parsedData,
|
|
87
|
-
channel);
|
|
88
|
-
break;
|
|
89
|
-
case SPLIT_KILL:
|
|
90
|
-
pushEmitter.emit(SPLIT_KILL,
|
|
91
|
-
parsedData.changeNumber,
|
|
92
|
-
parsedData.splitName,
|
|
93
|
-
parsedData.defaultTreatment);
|
|
83
|
+
pushEmitter.emit(parsedData.type, parsedData, channel);
|
|
94
84
|
break;
|
|
95
85
|
|
|
96
86
|
/* occupancy & control events, handled by NotificationManagerKeeper */
|
|
@@ -100,6 +90,7 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
|
|
|
100
90
|
case CONTROL:
|
|
101
91
|
notificationKeeper.handleControlEvent(parsedData.controlType, channel, timestamp);
|
|
102
92
|
break;
|
|
93
|
+
|
|
103
94
|
default:
|
|
104
95
|
break;
|
|
105
96
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ControlType } from '../constants';
|
|
2
|
-
import { MY_SEGMENTS_UPDATE, SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY } from '../types';
|
|
2
|
+
import { MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY } from '../types';
|
|
3
3
|
|
|
4
4
|
export interface IMySegmentsUpdateData {
|
|
5
5
|
type: MY_SEGMENTS_UPDATE,
|
|
@@ -8,6 +8,33 @@ export interface IMySegmentsUpdateData {
|
|
|
8
8
|
segmentList?: string[]
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export enum Compression {
|
|
12
|
+
None = 0,
|
|
13
|
+
Gzip = 1,
|
|
14
|
+
Zlib = 2
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export enum UpdateStrategy {
|
|
18
|
+
UnboundedFetchRequest = 0,
|
|
19
|
+
BoundedFetchRequest = 1,
|
|
20
|
+
KeyList = 2,
|
|
21
|
+
SegmentRemoval = 3
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface KeyList {
|
|
25
|
+
a?: string[], // decimal hash64 of user keys
|
|
26
|
+
r?: string[], // decimal hash64 of user keys
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IMySegmentsUpdateV2Data {
|
|
30
|
+
type: MY_SEGMENTS_UPDATE_V2,
|
|
31
|
+
changeNumber: number,
|
|
32
|
+
segmentName: string,
|
|
33
|
+
c: Compression,
|
|
34
|
+
d: string,
|
|
35
|
+
u: UpdateStrategy,
|
|
36
|
+
}
|
|
37
|
+
|
|
11
38
|
export interface ISegmentUpdateData {
|
|
12
39
|
type: SEGMENT_UPDATE,
|
|
13
40
|
changeNumber: number,
|
|
@@ -38,6 +65,13 @@ export interface IOccupancyData {
|
|
|
38
65
|
}
|
|
39
66
|
}
|
|
40
67
|
|
|
41
|
-
export type INotificationData = IMySegmentsUpdateData | ISegmentUpdateData | ISplitUpdateData | ISplitKillData | IControlData | IOccupancyData
|
|
68
|
+
export type INotificationData = IMySegmentsUpdateData | IMySegmentsUpdateV2Data | ISegmentUpdateData | ISplitUpdateData | ISplitKillData | IControlData | IOccupancyData
|
|
42
69
|
export type INotificationMessage = { parsedData: INotificationData, channel: string, timestamp: number, data: string }
|
|
43
|
-
export type INotificationError = Event & { parsedData?: any }
|
|
70
|
+
export type INotificationError = Event & { parsedData?: any, message?: string }
|
|
71
|
+
|
|
72
|
+
export type SegmentsData = string[] | {
|
|
73
|
+
/* segment name */
|
|
74
|
+
name: string,
|
|
75
|
+
/* action: `true` for add, and `false` for delete */
|
|
76
|
+
add: boolean
|
|
77
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ISegmentsSyncTask } from '../../polling/types';
|
|
2
2
|
import Backoff from '../../../utils/Backoff';
|
|
3
3
|
import { IUpdateWorker } from './types';
|
|
4
|
+
import { SegmentsData } from '../SSEHandler/types';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* MySegmentsUpdateWorker class
|
|
@@ -10,7 +11,7 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
|
|
|
10
11
|
private readonly mySegmentsSyncTask: ISegmentsSyncTask;
|
|
11
12
|
private maxChangeNumber: number;
|
|
12
13
|
private handleNewEvent: boolean;
|
|
13
|
-
private
|
|
14
|
+
private segmentsData?: SegmentsData;
|
|
14
15
|
private currentChangeNumber: number;
|
|
15
16
|
readonly backoff: Backoff;
|
|
16
17
|
|
|
@@ -21,7 +22,7 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
|
|
|
21
22
|
this.mySegmentsSyncTask = mySegmentsSyncTask;
|
|
22
23
|
this.maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
|
|
23
24
|
this.handleNewEvent = false;
|
|
24
|
-
this.
|
|
25
|
+
this.segmentsData = undefined; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
|
|
25
26
|
this.currentChangeNumber = -1; // @TODO: remove once `/mySegments` endpoint provides the changeNumber
|
|
26
27
|
this.put = this.put.bind(this);
|
|
27
28
|
this.__handleMySegmentsUpdateCall = this.__handleMySegmentsUpdateCall.bind(this);
|
|
@@ -36,7 +37,7 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
|
|
|
36
37
|
const currentMaxChangeNumber = this.maxChangeNumber;
|
|
37
38
|
|
|
38
39
|
// fetch mySegments revalidating data if cached
|
|
39
|
-
this.mySegmentsSyncTask.execute(this.
|
|
40
|
+
this.mySegmentsSyncTask.execute(this.segmentsData, true).then((result) => {
|
|
40
41
|
if (result !== false) // Unlike `Splits|SegmentsUpdateWorker`, we cannot use `mySegmentsCache.getChangeNumber` since `/mySegments` endpoint doesn't provide this value.
|
|
41
42
|
this.currentChangeNumber = Math.max(this.currentChangeNumber, currentMaxChangeNumber); // use `currentMaxChangeNumber`, in case that `this.maxChangeNumber` was updated during fetch.
|
|
42
43
|
if (this.handleNewEvent) {
|
|
@@ -52,15 +53,15 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
|
|
|
52
53
|
* Invoked by NotificationProcessor on MY_SEGMENTS_UPDATE event
|
|
53
54
|
*
|
|
54
55
|
* @param {number} changeNumber change number of the MY_SEGMENTS_UPDATE notification
|
|
55
|
-
* @param {
|
|
56
|
+
* @param {SegmentsData | undefined} segmentsData might be undefined
|
|
56
57
|
*/
|
|
57
|
-
put(changeNumber: number,
|
|
58
|
+
put(changeNumber: number, segmentsData?: SegmentsData) {
|
|
58
59
|
if (changeNumber <= this.currentChangeNumber || changeNumber <= this.maxChangeNumber) return;
|
|
59
60
|
|
|
60
61
|
this.maxChangeNumber = changeNumber;
|
|
61
62
|
this.handleNewEvent = true;
|
|
62
63
|
this.backoff.reset();
|
|
63
|
-
this.
|
|
64
|
+
this.segmentsData = segmentsData;
|
|
64
65
|
|
|
65
66
|
if (this.mySegmentsSyncTask.isExecuting()) return;
|
|
66
67
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ISegmentsCacheSync } from '../../../storages/types';
|
|
2
2
|
import Backoff from '../../../utils/Backoff';
|
|
3
3
|
import { ISegmentsSyncTask } from '../../polling/types';
|
|
4
|
+
import { ISegmentUpdateData } from '../SSEHandler/types';
|
|
4
5
|
import { IUpdateWorker } from './types';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -66,7 +67,7 @@ export default class SegmentsUpdateWorker implements IUpdateWorker {
|
|
|
66
67
|
* @param {number} changeNumber change number of the SEGMENT_UPDATE notification
|
|
67
68
|
* @param {string} segmentName segment name of the SEGMENT_UPDATE notification
|
|
68
69
|
*/
|
|
69
|
-
put(changeNumber
|
|
70
|
+
put({ changeNumber, segmentName }: ISegmentUpdateData) {
|
|
70
71
|
const currentChangeNumber = this.segmentsCache.getChangeNumber(segmentName);
|
|
71
72
|
|
|
72
73
|
if (changeNumber <= currentChangeNumber || changeNumber <= this.maxChangeNumbers[segmentName]) return;
|
|
@@ -3,6 +3,7 @@ import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
|
3
3
|
import { ISplitsCacheSync } from '../../../storages/types';
|
|
4
4
|
import Backoff from '../../../utils/Backoff';
|
|
5
5
|
import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
|
|
6
|
+
import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
|
|
6
7
|
import { IUpdateWorker } from './types';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -60,7 +61,7 @@ export default class SplitsUpdateWorker implements IUpdateWorker {
|
|
|
60
61
|
*
|
|
61
62
|
* @param {number} changeNumber change number of the SPLIT_UPDATE notification
|
|
62
63
|
*/
|
|
63
|
-
put(changeNumber:
|
|
64
|
+
put({ changeNumber }: Pick<ISplitUpdateData, 'changeNumber'>) {
|
|
64
65
|
const currentChangeNumber = this.splitsCache.getChangeNumber();
|
|
65
66
|
|
|
66
67
|
if (changeNumber <= currentChangeNumber || changeNumber <= this.maxChangeNumber) return;
|
|
@@ -81,14 +82,14 @@ export default class SplitsUpdateWorker implements IUpdateWorker {
|
|
|
81
82
|
* @param {string} splitName name of split to kill
|
|
82
83
|
* @param {string} defaultTreatment default treatment value
|
|
83
84
|
*/
|
|
84
|
-
killSplit(changeNumber
|
|
85
|
+
killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
|
|
85
86
|
// @TODO handle retry due to errors in storage, once we allow the definition of custom async storages
|
|
86
87
|
if (this.splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
|
|
87
88
|
// trigger an SDK_UPDATE if Split was killed locally
|
|
88
89
|
this.splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
|
|
89
90
|
}
|
|
90
91
|
// queues the SplitChanges fetch (only if changeNumber is newer)
|
|
91
|
-
this.put(changeNumber);
|
|
92
|
+
this.put({ changeNumber });
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
}
|
|
@@ -26,6 +26,7 @@ export const PUSH_SUBSYSTEM_DOWN = 'PUSH_SUBSYSTEM_DOWN';
|
|
|
26
26
|
|
|
27
27
|
// Update-type push notifications, handled by NotificationProcessor
|
|
28
28
|
export const MY_SEGMENTS_UPDATE = 'MY_SEGMENTS_UPDATE';
|
|
29
|
+
export const MY_SEGMENTS_UPDATE_V2 = 'MY_SEGMENTS_UPDATE_V2';
|
|
29
30
|
export const SEGMENT_UPDATE = 'SEGMENT_UPDATE';
|
|
30
31
|
export const SPLIT_KILL = 'SPLIT_KILL';
|
|
31
32
|
export const SPLIT_UPDATE = 'SPLIT_UPDATE';
|
|
@@ -38,4 +39,5 @@ export enum ControlType {
|
|
|
38
39
|
STREAMING_DISABLED = 'STREAMING_DISABLED',
|
|
39
40
|
STREAMING_PAUSED = 'STREAMING_PAUSED',
|
|
40
41
|
STREAMING_RESUMED = 'STREAMING_RESUMED',
|
|
42
|
+
STREAMING_RESET = 'STREAMING_RESET',
|
|
41
43
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { algorithms } from '../../utils/decompress';
|
|
2
|
+
import { decodeFromBase64 } from '../../utils/base64';
|
|
3
|
+
import { Compression, KeyList } from './SSEHandler/types';
|
|
4
|
+
|
|
5
|
+
const GZIP = 1;
|
|
6
|
+
const ZLIB = 2;
|
|
7
|
+
|
|
8
|
+
function Uint8ArrayToString(myUint8Arr: Uint8Array) { // @ts-ignore
|
|
9
|
+
return String.fromCharCode.apply(null, myUint8Arr);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function StringToUint8Array(myString: string) {
|
|
13
|
+
const charCodes = myString.split('').map((e) => e.charCodeAt(0));
|
|
14
|
+
return new Uint8Array(charCodes);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Decode and decompress 'data' with 'compression' algorithm
|
|
19
|
+
*
|
|
20
|
+
* @param {string} data
|
|
21
|
+
* @param {number} compression 1 GZIP, 2 ZLIB
|
|
22
|
+
* @returns {Uint8Array}
|
|
23
|
+
* @throws if data string cannot be decoded, decompressed or the provided compression value is invalid (not 1 or 2)
|
|
24
|
+
*/
|
|
25
|
+
function decompress(data: string, compression: Compression) {
|
|
26
|
+
let compressData = decodeFromBase64(data);
|
|
27
|
+
const binData = StringToUint8Array(compressData);
|
|
28
|
+
|
|
29
|
+
if (typeof algorithms === 'string') throw new Error(algorithms);
|
|
30
|
+
if (compression === GZIP) return algorithms.gunzipSync(binData);
|
|
31
|
+
if (compression === ZLIB) return algorithms.unzlibSync(binData);
|
|
32
|
+
throw new Error(`Invalid compression algorithm #${compression}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Decode, decompress and parse the provided 'data' into a KeyList object
|
|
37
|
+
*
|
|
38
|
+
* @param {string} data
|
|
39
|
+
* @param {number} compression
|
|
40
|
+
* @returns {{a?: string[], r?: string[] }}
|
|
41
|
+
* @throws if data string cannot be decoded, decompressed or parsed
|
|
42
|
+
*/
|
|
43
|
+
export function parseKeyList(data: string, compression: Compression): KeyList {
|
|
44
|
+
const binKeyList = decompress(data, compression);
|
|
45
|
+
const strKeyList = Uint8ArrayToString(binKeyList);
|
|
46
|
+
|
|
47
|
+
// replace numbers to strings, to avoid losing precision
|
|
48
|
+
return JSON.parse(strKeyList.replace(/\d+/g, '"$&"'));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Decode, decompress and parse the provided 'data' into a Bitmap object
|
|
53
|
+
*
|
|
54
|
+
* @param {string} data
|
|
55
|
+
* @param {number} compression
|
|
56
|
+
* @returns {Uint8Array}
|
|
57
|
+
* @throws if data string cannot be decoded or decompressed
|
|
58
|
+
*/
|
|
59
|
+
export function parseBitmap(data: string, compression: Compression) {
|
|
60
|
+
return decompress(data, compression);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if the 'bitmap' bit at 'hash64hex' position is 1
|
|
65
|
+
*
|
|
66
|
+
* @param {Uint8Array} bitmap
|
|
67
|
+
* @param {string} hash64hex 16-chars string, representing a number in hexa
|
|
68
|
+
* @returns {boolean}
|
|
69
|
+
*/
|
|
70
|
+
export function isInBitmap(bitmap: Uint8Array, hash64hex: string) {
|
|
71
|
+
// using the lowest 32 bits as index, to avoid losing precision when converting to number
|
|
72
|
+
const index = parseInt(hash64hex.slice(8), 16) % (bitmap.length * 8);
|
|
73
|
+
|
|
74
|
+
const internal = Math.floor(index / 8);
|
|
75
|
+
const offset = index % 8;
|
|
76
|
+
return (bitmap[internal] & 1 << offset) > 0;
|
|
77
|
+
}
|
|
@@ -3,7 +3,6 @@ import { ISSEClient } from './SSEClient/types';
|
|
|
3
3
|
import { IStorageSync } from '../../storages/types';
|
|
4
4
|
import { IReadinessManager } from '../../readiness/types';
|
|
5
5
|
import { ISegmentsSyncTask, IPollingManager } from '../polling/types';
|
|
6
|
-
import { IUpdateWorker } from './UpdateWorkers/types';
|
|
7
6
|
import objectAssign from 'object-assign';
|
|
8
7
|
import Backoff from '../../utils/Backoff';
|
|
9
8
|
import SSEHandlerFactory from './SSEHandler';
|
|
@@ -16,9 +15,14 @@ import SSEClient from './SSEClient';
|
|
|
16
15
|
import { IFetchAuth } from '../../services/types';
|
|
17
16
|
import { ISettings } from '../../types';
|
|
18
17
|
import { getMatching } from '../../utils/key';
|
|
19
|
-
import { MY_SEGMENTS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP } from './constants';
|
|
18
|
+
import { MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants';
|
|
20
19
|
import { IPlatform } from '../../sdkFactory/types';
|
|
21
|
-
import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT } from '../../logger/constants';
|
|
20
|
+
import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 } from '../../logger/constants';
|
|
21
|
+
import { KeyList, UpdateStrategy } from './SSEHandler/types';
|
|
22
|
+
import { isInBitmap, parseBitmap, parseKeyList } from './mySegmentsV2utils';
|
|
23
|
+
import { ISet, _Set } from '../../utils/lang/sets';
|
|
24
|
+
import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64';
|
|
25
|
+
import { IAuthTokenPushEnabled } from './AuthClient/types';
|
|
22
26
|
|
|
23
27
|
/**
|
|
24
28
|
* PushManager factory:
|
|
@@ -54,53 +58,67 @@ export default function pushManagerFactory(
|
|
|
54
58
|
const sseHandler = SSEHandlerFactory(log, pushEmitter);
|
|
55
59
|
sseClient.setEventHandler(sseHandler);
|
|
56
60
|
|
|
61
|
+
// init workers
|
|
62
|
+
const segmentsUpdateWorker = userKey ? new MySegmentsUpdateWorker(pollingManager.segmentsSyncTask) : new SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
|
|
63
|
+
// For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
|
|
64
|
+
const splitsUpdateWorker = new SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
|
|
65
|
+
|
|
57
66
|
// [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
|
|
58
67
|
const userKeyHashes: Record<string, string> = {};
|
|
68
|
+
// [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
|
|
69
|
+
// Hash64 is used to process MY_SEGMENTS_UPDATE_V2 events and dispatch actions to the corresponding MySegmentsUpdateWorker.
|
|
70
|
+
const clients: Record<string, { hash64: Hash64, worker: MySegmentsUpdateWorker }> = {};
|
|
59
71
|
if (userKey) {
|
|
60
72
|
const hash = hashUserKey(userKey);
|
|
61
73
|
userKeyHashes[hash] = userKey;
|
|
74
|
+
clients[userKey] = { hash64: hash64(userKey), worker: segmentsUpdateWorker as MySegmentsUpdateWorker };
|
|
62
75
|
}
|
|
63
76
|
|
|
64
|
-
// init workers
|
|
65
|
-
const segmentsUpdateWorker = userKey ? new MySegmentsUpdateWorker(pollingManager.segmentsSyncTask) : new SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
|
|
66
|
-
// [Only for server-side] we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
|
|
67
|
-
const splitsUpdateWorker = new SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
|
|
68
|
-
// [Only for client-side] map of user keys to their corresponding MySegmentsUpdateWorkers. It has a two-fold intention:
|
|
69
|
-
// - stop workers all together when push is disconnected
|
|
70
|
-
// - keep the current list of user keys to authenticate
|
|
71
|
-
const workers: Record<string, IUpdateWorker> = {};
|
|
72
|
-
if (userKey) workers[userKey] = segmentsUpdateWorker;
|
|
73
|
-
|
|
74
77
|
// [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
|
|
75
78
|
let connectForNewClient = false;
|
|
76
79
|
|
|
77
|
-
// flag that indicates if `disconnectPush` was called, either by the SyncManager
|
|
80
|
+
// flag that indicates if `stop/disconnectPush` was called, either by the SyncManager, when the client is destroyed, or due to a PUSH_NONRETRYABLE_ERROR error.
|
|
78
81
|
// It is used to halt the `connectPush` process if it was in progress.
|
|
79
82
|
let disconnected: boolean | undefined;
|
|
83
|
+
// flag that indicates a PUSH_NONRETRYABLE_ERROR, condition with which starting pushManager again is ignored.
|
|
84
|
+
let disabled: boolean | undefined; // `disabled` implies `disconnected === true`
|
|
80
85
|
|
|
81
86
|
/** PushManager functions related to initialization */
|
|
82
87
|
|
|
83
88
|
const connectPushRetryBackoff = new Backoff(connectPush, settings.scheduler.pushRetryBackoffBase);
|
|
84
89
|
|
|
85
|
-
let
|
|
90
|
+
let timeoutIdTokenRefresh: ReturnType<typeof setTimeout>;
|
|
91
|
+
let timeoutIdSseOpen: ReturnType<typeof setTimeout>;
|
|
92
|
+
|
|
93
|
+
function scheduleTokenRefreshAndSse(authData: IAuthTokenPushEnabled) {
|
|
94
|
+
// clear scheduled tasks if exist
|
|
95
|
+
if (timeoutIdTokenRefresh) clearTimeout(timeoutIdTokenRefresh);
|
|
96
|
+
if (timeoutIdSseOpen) clearTimeout(timeoutIdSseOpen);
|
|
86
97
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
// Set token refresh 10 minutes before `expirationTime - issuedAt`
|
|
99
|
+
const decodedToken = authData.decodedToken;
|
|
100
|
+
const refreshTokenDelay = decodedToken.exp - decodedToken.iat - SECONDS_BEFORE_EXPIRATION;
|
|
101
|
+
// Default connDelay of 60 secs
|
|
102
|
+
const connDelay = typeof authData.connDelay === 'number' && authData.connDelay >= 0 ? authData.connDelay : 60;
|
|
90
103
|
|
|
91
|
-
|
|
92
|
-
const delayInSeconds = expirationTime - issuedAt - SECONDS_BEFORE_EXPIRATION;
|
|
104
|
+
log.info(STREAMING_REFRESH_TOKEN, [refreshTokenDelay, connDelay]);
|
|
93
105
|
|
|
94
|
-
|
|
106
|
+
timeoutIdTokenRefresh = setTimeout(connectPush, refreshTokenDelay * 1000);
|
|
95
107
|
|
|
96
|
-
|
|
108
|
+
timeoutIdSseOpen = setTimeout(() => {
|
|
109
|
+
// halt if disconnected
|
|
110
|
+
if (disconnected) return;
|
|
111
|
+
sseClient.open(authData);
|
|
112
|
+
}, connDelay * 1000);
|
|
97
113
|
}
|
|
98
114
|
|
|
99
115
|
function connectPush() {
|
|
116
|
+
// Guard condition in case `stop/disconnectPush` has been called (e.g., calling SDK destroy, or app signal close/background)
|
|
117
|
+
if (disconnected) return;
|
|
118
|
+
log.info(STREAMING_CONNECTING, [disconnected === undefined ? '' : 'Re-']);
|
|
100
119
|
disconnected = false;
|
|
101
|
-
log.info(STREAMING_CONNECTING);
|
|
102
120
|
|
|
103
|
-
const userKeys = userKey ? Object.keys(
|
|
121
|
+
const userKeys = userKey ? Object.keys(clients) : undefined;
|
|
104
122
|
authenticate(userKeys).then(
|
|
105
123
|
function (authData) {
|
|
106
124
|
if (disconnected) return;
|
|
@@ -114,12 +132,10 @@ export default function pushManagerFactory(
|
|
|
114
132
|
}
|
|
115
133
|
|
|
116
134
|
// [Only for client-side] don't open SSE connection if a new shared client was added, since it means that a new authentication is taking place
|
|
117
|
-
if (userKeys && userKeys.length < Object.keys(
|
|
135
|
+
if (userKeys && userKeys.length < Object.keys(clients).length) return;
|
|
118
136
|
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
sseClient.open(authData);
|
|
122
|
-
scheduleTokenRefresh(decodedToken.iat, decodedToken.exp);
|
|
137
|
+
// Schedule SSE connection and refresh token
|
|
138
|
+
scheduleTokenRefreshAndSse(authData);
|
|
123
139
|
}
|
|
124
140
|
).catch(
|
|
125
141
|
function (error) {
|
|
@@ -141,11 +157,15 @@ export default function pushManagerFactory(
|
|
|
141
157
|
|
|
142
158
|
// close SSE connection and cancel scheduled tasks
|
|
143
159
|
function disconnectPush() {
|
|
144
|
-
|
|
160
|
+
// Halt disconnecting, just to avoid redundant logs if called multiple times
|
|
161
|
+
if (disconnected) return;
|
|
145
162
|
disconnected = true;
|
|
163
|
+
|
|
164
|
+
sseClient.close();
|
|
146
165
|
log.info(STREAMING_DISCONNECTING);
|
|
147
166
|
|
|
148
|
-
if (
|
|
167
|
+
if (timeoutIdTokenRefresh) clearTimeout(timeoutIdTokenRefresh);
|
|
168
|
+
if (timeoutIdSseOpen) clearTimeout(timeoutIdSseOpen);
|
|
149
169
|
connectPushRetryBackoff.reset();
|
|
150
170
|
|
|
151
171
|
stopWorkers();
|
|
@@ -154,18 +174,23 @@ export default function pushManagerFactory(
|
|
|
154
174
|
// cancel scheduled fetch retries of Splits, Segments, and MySegments Update Workers
|
|
155
175
|
function stopWorkers() {
|
|
156
176
|
splitsUpdateWorker.backoff.reset();
|
|
157
|
-
if (userKey) forOwn(
|
|
177
|
+
if (userKey) forOwn(clients, ({ worker }) => worker.backoff.reset());
|
|
158
178
|
else segmentsUpdateWorker.backoff.reset();
|
|
159
179
|
}
|
|
160
180
|
|
|
161
181
|
pushEmitter.on(PUSH_SUBSYSTEM_DOWN, stopWorkers);
|
|
162
182
|
|
|
163
|
-
//
|
|
164
|
-
|
|
183
|
+
// Only required when streaming connects after a PUSH_RETRYABLE_ERROR.
|
|
184
|
+
// Otherwise it is unnecessary (e.g, STREAMING_RESUMED).
|
|
185
|
+
pushEmitter.on(PUSH_SUBSYSTEM_UP, () => {
|
|
186
|
+
connectPushRetryBackoff.reset();
|
|
187
|
+
stopWorkers();
|
|
188
|
+
});
|
|
165
189
|
|
|
166
190
|
/** Fallbacking without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */
|
|
167
191
|
|
|
168
192
|
pushEmitter.on(PUSH_NONRETRYABLE_ERROR, function handleNonRetryableError() {
|
|
193
|
+
disabled = true;
|
|
169
194
|
// Note: `stopWorkers` is been called twice, but it is not harmful
|
|
170
195
|
disconnectPush();
|
|
171
196
|
pushEmitter.emit(PUSH_SUBSYSTEM_DOWN); // no harm if polling already
|
|
@@ -185,21 +210,93 @@ export default function pushManagerFactory(
|
|
|
185
210
|
pushEmitter.emit(PUSH_SUBSYSTEM_DOWN); // no harm if polling already
|
|
186
211
|
});
|
|
187
212
|
|
|
213
|
+
/** STREAMING_RESET notification. Unlike a PUSH_RETRYABLE_ERROR, it doesn't emit PUSH_SUBSYSTEM_DOWN to fallback polling */
|
|
214
|
+
|
|
215
|
+
pushEmitter.on(ControlType.STREAMING_RESET, function handleStreamingReset() {
|
|
216
|
+
if (disconnected) return; // should never happen
|
|
217
|
+
|
|
218
|
+
// Minimum required clean-up.
|
|
219
|
+
// `disconnectPush` cannot be called because it sets `disconnected` and thus `connectPush` will not execute
|
|
220
|
+
if (timeoutIdTokenRefresh) clearTimeout(timeoutIdTokenRefresh);
|
|
221
|
+
|
|
222
|
+
connectPush();
|
|
223
|
+
});
|
|
224
|
+
|
|
188
225
|
/** Functions related to synchronization (Queues and Workers in the spec) */
|
|
189
226
|
|
|
190
227
|
pushEmitter.on(SPLIT_KILL, splitsUpdateWorker.killSplit);
|
|
191
228
|
pushEmitter.on(SPLIT_UPDATE, splitsUpdateWorker.put);
|
|
229
|
+
|
|
192
230
|
if (userKey) {
|
|
193
231
|
pushEmitter.on(MY_SEGMENTS_UPDATE, function handleMySegmentsUpdate(parsedData, channel) {
|
|
194
232
|
const userKeyHash = channel.split('_')[2];
|
|
195
233
|
const userKey = userKeyHashes[userKeyHash];
|
|
196
|
-
if (userKey &&
|
|
197
|
-
|
|
198
|
-
mySegmentsUpdateWorker.put(
|
|
234
|
+
if (userKey && clients[userKey]) { // check existence since it can be undefined if client has been destroyed
|
|
235
|
+
clients[userKey].worker.put(
|
|
199
236
|
parsedData.changeNumber,
|
|
200
237
|
parsedData.includesPayload ? parsedData.segmentList ? parsedData.segmentList : [] : undefined);
|
|
201
238
|
}
|
|
202
239
|
});
|
|
240
|
+
pushEmitter.on(MY_SEGMENTS_UPDATE_V2, function handleMySegmentsUpdate(parsedData) {
|
|
241
|
+
switch (parsedData.u) {
|
|
242
|
+
case UpdateStrategy.BoundedFetchRequest: {
|
|
243
|
+
let bitmap: Uint8Array;
|
|
244
|
+
try {
|
|
245
|
+
bitmap = parseBitmap(parsedData.d, parsedData.c);
|
|
246
|
+
} catch (e) {
|
|
247
|
+
log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['BoundedFetchRequest', e]);
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
forOwn(clients, ({ hash64, worker }) => {
|
|
252
|
+
if (isInBitmap(bitmap, hash64.hex)) {
|
|
253
|
+
worker.put(parsedData.changeNumber); // fetch mySegments
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
case UpdateStrategy.KeyList: {
|
|
259
|
+
let keyList: KeyList, added: ISet<string>, removed: ISet<string>;
|
|
260
|
+
try {
|
|
261
|
+
keyList = parseKeyList(parsedData.d, parsedData.c);
|
|
262
|
+
added = new _Set(keyList.a);
|
|
263
|
+
removed = new _Set(keyList.r);
|
|
264
|
+
} catch (e) {
|
|
265
|
+
log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['KeyList', e]);
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
forOwn(clients, ({ hash64, worker }) => {
|
|
270
|
+
const add = added.has(hash64.dec) ? true : removed.has(hash64.dec) ? false : undefined;
|
|
271
|
+
if (add !== undefined) {
|
|
272
|
+
worker.put(parsedData.changeNumber, {
|
|
273
|
+
name: parsedData.segmentName,
|
|
274
|
+
add
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
case UpdateStrategy.SegmentRemoval:
|
|
281
|
+
if (!parsedData.segmentName) {
|
|
282
|
+
log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['SegmentRemoval', 'No segment name was provided']);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
forOwn(clients, ({ worker }) =>
|
|
287
|
+
worker.put(parsedData.changeNumber, {
|
|
288
|
+
name: parsedData.segmentName,
|
|
289
|
+
add: false
|
|
290
|
+
})
|
|
291
|
+
);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// `UpdateStrategy.UnboundedFetchRequest` and fallbacks of other cases
|
|
296
|
+
forOwn(clients, ({ worker }) => {
|
|
297
|
+
worker.put(parsedData.changeNumber);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
203
300
|
} else {
|
|
204
301
|
pushEmitter.on(SEGMENT_UPDATE, (segmentsUpdateWorker as SegmentsUpdateWorker).put);
|
|
205
302
|
}
|
|
@@ -209,11 +306,11 @@ export default function pushManagerFactory(
|
|
|
209
306
|
Object.create(pushEmitter),
|
|
210
307
|
{
|
|
211
308
|
// Expose functionality for starting and stoping push mode:
|
|
212
|
-
stop: disconnectPush, // `handleNonRetryableError` cannot be used as `stop`, because it emits PUSH_SUBSYSTEM_DOWN event, which
|
|
309
|
+
stop: disconnectPush, // `handleNonRetryableError` cannot be used as `stop`, because it emits PUSH_SUBSYSTEM_DOWN event, which starts polling.
|
|
213
310
|
|
|
214
311
|
start() {
|
|
215
|
-
// Guard condition to avoid calling `connectPush` again if the `start` method is called multiple times.
|
|
216
|
-
if (disconnected === false) return;
|
|
312
|
+
// Guard condition to avoid calling `connectPush` again if the `start` method is called multiple times or if push has been disabled.
|
|
313
|
+
if (disabled || disconnected === false) return;
|
|
217
314
|
disconnected = false;
|
|
218
315
|
// Run in next event-loop cycle for optimization on client-side: if multiple clients are created in the same cycle than the factory, only one authentication is performed.
|
|
219
316
|
setTimeout(connectPush);
|
|
@@ -221,8 +318,7 @@ export default function pushManagerFactory(
|
|
|
221
318
|
|
|
222
319
|
// [Only for client-side]
|
|
223
320
|
add(userKey: string, mySegmentsSyncTask: ISegmentsSyncTask) {
|
|
224
|
-
|
|
225
|
-
workers[userKey] = mySegmentsUpdateWorker;
|
|
321
|
+
clients[userKey] = { hash64: hash64(userKey), worker: new MySegmentsUpdateWorker(mySegmentsSyncTask) };
|
|
226
322
|
|
|
227
323
|
const hash = hashUserKey(userKey);
|
|
228
324
|
|
|
@@ -245,6 +341,7 @@ export default function pushManagerFactory(
|
|
|
245
341
|
remove(userKey: string) {
|
|
246
342
|
const hash = hashUserKey(userKey);
|
|
247
343
|
delete userKeyHashes[hash];
|
|
344
|
+
delete clients[userKey];
|
|
248
345
|
}
|
|
249
346
|
}
|
|
250
347
|
);
|