@splitsoftware/splitio-commons 0.1.1-canary.6 → 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 +5 -2
- package/cjs/listeners/node.js +9 -2
- 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 -1
- package/cjs/services/splitApi.js +9 -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/fetchers/segmentChangesFetcher.js +0 -8
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +30 -10
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +2 -4
- package/cjs/sync/streaming/SSEClient/index.js +38 -20
- 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 +148 -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 +18 -5
- package/cjs/sync/syncTask.js +1 -1
- 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 +5 -2
- package/esm/listeners/node.js +9 -2
- 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 -1
- package/esm/services/splitApi.js +9 -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/fetchers/segmentChangesFetcher.js +0 -8
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +30 -10
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +2 -4
- package/esm/sync/streaming/SSEClient/index.js +38 -20
- 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 +150 -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 +18 -5
- package/esm/sync/syncTask.js +1 -1
- 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 +5 -2
- package/src/listeners/node.ts +14 -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 -1
- package/src/sdkFactory/types.ts +4 -5
- package/src/services/splitApi.ts +12 -3
- package/src/services/splitHttpClient.ts +6 -5
- package/src/services/types.ts +7 -3
- 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/SegmentsCachePluggable.ts +1 -1
- 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/fetchers/segmentChangesFetcher.ts +0 -7
- package/src/sync/polling/types.ts +2 -1
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +28 -10
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +2 -3
- package/src/sync/streaming/AuthClient/types.ts +3 -0
- package/src/sync/streaming/SSEClient/index.ts +43 -23
- 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 +145 -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 +19 -5
- package/src/sync/syncTask.ts +1 -1
- package/src/sync/types.ts +3 -1
- 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/lang/index.ts +1 -1
- 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 +3 -3
- package/types/listeners/node.d.ts +3 -2
- package/types/logger/constants.d.ts +2 -0
- package/types/sdkFactory/types.d.ts +4 -5
- package/types/services/types.d.ts +4 -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 +9 -12
- 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/sync/syncTask.d.ts +1 -1
- package/types/sync/types.d.ts +2 -0
- 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/lang/index.d.ts +1 -1
- 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 -178
- package/cjs/sync/streaming/pushManagerSS.js +0 -128
- package/esm/sync/streaming/pushManagerCS.js +0 -174
- package/esm/sync/streaming/pushManagerSS.js +0 -124
- package/src/sync/streaming/pushManagerCS.ts +0 -237
- package/src/sync/streaming/pushManagerSS.ts +0 -177
|
@@ -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:
|
|
@@ -34,11 +38,15 @@ export default function pushManagerFactory(
|
|
|
34
38
|
settings: ISettings,
|
|
35
39
|
): IPushManagerCS | undefined {
|
|
36
40
|
|
|
41
|
+
// `userKey` is the matching key of main client in client-side SDK.
|
|
42
|
+
// It can be used to check if running on client-side or server-side SDK.
|
|
43
|
+
const userKey = settings.core.key ? getMatching(settings.core.key) : undefined; //
|
|
37
44
|
const log = settings.log;
|
|
38
45
|
|
|
39
46
|
let sseClient: ISSEClient;
|
|
40
47
|
try {
|
|
41
|
-
|
|
48
|
+
// `useHeaders` false for client-side, even if the platform EventSource supports headers (e.g., React Native).
|
|
49
|
+
sseClient = new SSEClient(settings, userKey ? false : true, platform.getEventSource);
|
|
42
50
|
} catch (e) {
|
|
43
51
|
log.warn(STREAMING_FALLBACK, [e]);
|
|
44
52
|
return;
|
|
@@ -50,54 +58,67 @@ export default function pushManagerFactory(
|
|
|
50
58
|
const sseHandler = SSEHandlerFactory(log, pushEmitter);
|
|
51
59
|
sseClient.setEventHandler(sseHandler);
|
|
52
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
|
+
|
|
53
66
|
// [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
|
|
54
67
|
const userKeyHashes: Record<string, string> = {};
|
|
55
|
-
|
|
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 }> = {};
|
|
56
71
|
if (userKey) {
|
|
57
72
|
const hash = hashUserKey(userKey);
|
|
58
73
|
userKeyHashes[hash] = userKey;
|
|
74
|
+
clients[userKey] = { hash64: hash64(userKey), worker: segmentsUpdateWorker as MySegmentsUpdateWorker };
|
|
59
75
|
}
|
|
60
76
|
|
|
61
|
-
// init workers
|
|
62
|
-
const segmentsUpdateWorker = userKey ? new MySegmentsUpdateWorker(pollingManager.segmentsSyncTask) : new SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
|
|
63
|
-
// [Only 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
|
-
// [Only for client-side] map of user keys to their corresponding MySegmentsUpdateWorkers. It has a two-fold intention:
|
|
66
|
-
// - stop workers all together when push is disconnected
|
|
67
|
-
// - keep the current list of user keys to authenticate
|
|
68
|
-
const workers: Record<string, IUpdateWorker> = {};
|
|
69
|
-
if (userKey) workers[userKey] = segmentsUpdateWorker;
|
|
70
|
-
|
|
71
77
|
// [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
|
|
72
78
|
let connectForNewClient = false;
|
|
73
79
|
|
|
74
|
-
// 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.
|
|
75
81
|
// It is used to halt the `connectPush` process if it was in progress.
|
|
76
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`
|
|
77
85
|
|
|
78
86
|
/** PushManager functions related to initialization */
|
|
79
87
|
|
|
80
88
|
const connectPushRetryBackoff = new Backoff(connectPush, settings.scheduler.pushRetryBackoffBase);
|
|
81
89
|
|
|
82
|
-
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);
|
|
83
97
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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;
|
|
87
103
|
|
|
88
|
-
|
|
89
|
-
const delayInSeconds = expirationTime - issuedAt - SECONDS_BEFORE_EXPIRATION;
|
|
104
|
+
log.info(STREAMING_REFRESH_TOKEN, [refreshTokenDelay, connDelay]);
|
|
90
105
|
|
|
91
|
-
|
|
106
|
+
timeoutIdTokenRefresh = setTimeout(connectPush, refreshTokenDelay * 1000);
|
|
92
107
|
|
|
93
|
-
|
|
108
|
+
timeoutIdSseOpen = setTimeout(() => {
|
|
109
|
+
// halt if disconnected
|
|
110
|
+
if (disconnected) return;
|
|
111
|
+
sseClient.open(authData);
|
|
112
|
+
}, connDelay * 1000);
|
|
94
113
|
}
|
|
95
114
|
|
|
96
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-']);
|
|
97
119
|
disconnected = false;
|
|
98
|
-
log.info(STREAMING_CONNECTING);
|
|
99
120
|
|
|
100
|
-
const userKeys = userKey ? Object.keys(
|
|
121
|
+
const userKeys = userKey ? Object.keys(clients) : undefined;
|
|
101
122
|
authenticate(userKeys).then(
|
|
102
123
|
function (authData) {
|
|
103
124
|
if (disconnected) return;
|
|
@@ -111,12 +132,10 @@ export default function pushManagerFactory(
|
|
|
111
132
|
}
|
|
112
133
|
|
|
113
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
|
|
114
|
-
if (userKeys && userKeys.length < Object.keys(
|
|
135
|
+
if (userKeys && userKeys.length < Object.keys(clients).length) return;
|
|
115
136
|
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
sseClient.open(authData);
|
|
119
|
-
scheduleTokenRefresh(decodedToken.iat, decodedToken.exp);
|
|
137
|
+
// Schedule SSE connection and refresh token
|
|
138
|
+
scheduleTokenRefreshAndSse(authData);
|
|
120
139
|
}
|
|
121
140
|
).catch(
|
|
122
141
|
function (error) {
|
|
@@ -138,11 +157,15 @@ export default function pushManagerFactory(
|
|
|
138
157
|
|
|
139
158
|
// close SSE connection and cancel scheduled tasks
|
|
140
159
|
function disconnectPush() {
|
|
160
|
+
// Halt disconnecting, just to avoid redundant logs if called multiple times
|
|
161
|
+
if (disconnected) return;
|
|
141
162
|
disconnected = true;
|
|
142
|
-
|
|
163
|
+
|
|
143
164
|
sseClient.close();
|
|
165
|
+
log.info(STREAMING_DISCONNECTING);
|
|
144
166
|
|
|
145
|
-
if (
|
|
167
|
+
if (timeoutIdTokenRefresh) clearTimeout(timeoutIdTokenRefresh);
|
|
168
|
+
if (timeoutIdSseOpen) clearTimeout(timeoutIdSseOpen);
|
|
146
169
|
connectPushRetryBackoff.reset();
|
|
147
170
|
|
|
148
171
|
stopWorkers();
|
|
@@ -151,18 +174,23 @@ export default function pushManagerFactory(
|
|
|
151
174
|
// cancel scheduled fetch retries of Splits, Segments, and MySegments Update Workers
|
|
152
175
|
function stopWorkers() {
|
|
153
176
|
splitsUpdateWorker.backoff.reset();
|
|
154
|
-
if (userKey) forOwn(
|
|
177
|
+
if (userKey) forOwn(clients, ({ worker }) => worker.backoff.reset());
|
|
155
178
|
else segmentsUpdateWorker.backoff.reset();
|
|
156
179
|
}
|
|
157
180
|
|
|
158
181
|
pushEmitter.on(PUSH_SUBSYSTEM_DOWN, stopWorkers);
|
|
159
182
|
|
|
160
|
-
//
|
|
161
|
-
|
|
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
|
+
});
|
|
162
189
|
|
|
163
190
|
/** Fallbacking without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */
|
|
164
191
|
|
|
165
192
|
pushEmitter.on(PUSH_NONRETRYABLE_ERROR, function handleNonRetryableError() {
|
|
193
|
+
disabled = true;
|
|
166
194
|
// Note: `stopWorkers` is been called twice, but it is not harmful
|
|
167
195
|
disconnectPush();
|
|
168
196
|
pushEmitter.emit(PUSH_SUBSYSTEM_DOWN); // no harm if polling already
|
|
@@ -182,21 +210,93 @@ export default function pushManagerFactory(
|
|
|
182
210
|
pushEmitter.emit(PUSH_SUBSYSTEM_DOWN); // no harm if polling already
|
|
183
211
|
});
|
|
184
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
|
+
|
|
185
225
|
/** Functions related to synchronization (Queues and Workers in the spec) */
|
|
186
226
|
|
|
187
227
|
pushEmitter.on(SPLIT_KILL, splitsUpdateWorker.killSplit);
|
|
188
228
|
pushEmitter.on(SPLIT_UPDATE, splitsUpdateWorker.put);
|
|
229
|
+
|
|
189
230
|
if (userKey) {
|
|
190
231
|
pushEmitter.on(MY_SEGMENTS_UPDATE, function handleMySegmentsUpdate(parsedData, channel) {
|
|
191
232
|
const userKeyHash = channel.split('_')[2];
|
|
192
233
|
const userKey = userKeyHashes[userKeyHash];
|
|
193
|
-
if (userKey &&
|
|
194
|
-
|
|
195
|
-
mySegmentsUpdateWorker.put(
|
|
234
|
+
if (userKey && clients[userKey]) { // check existence since it can be undefined if client has been destroyed
|
|
235
|
+
clients[userKey].worker.put(
|
|
196
236
|
parsedData.changeNumber,
|
|
197
237
|
parsedData.includesPayload ? parsedData.segmentList ? parsedData.segmentList : [] : undefined);
|
|
198
238
|
}
|
|
199
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
|
+
});
|
|
200
300
|
} else {
|
|
201
301
|
pushEmitter.on(SEGMENT_UPDATE, (segmentsUpdateWorker as SegmentsUpdateWorker).put);
|
|
202
302
|
}
|
|
@@ -206,17 +306,19 @@ export default function pushManagerFactory(
|
|
|
206
306
|
Object.create(pushEmitter),
|
|
207
307
|
{
|
|
208
308
|
// Expose functionality for starting and stoping push mode:
|
|
209
|
-
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.
|
|
210
310
|
|
|
211
311
|
start() {
|
|
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;
|
|
314
|
+
disconnected = false;
|
|
212
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.
|
|
213
316
|
setTimeout(connectPush);
|
|
214
317
|
},
|
|
215
318
|
|
|
216
319
|
// [Only for client-side]
|
|
217
320
|
add(userKey: string, mySegmentsSyncTask: ISegmentsSyncTask) {
|
|
218
|
-
|
|
219
|
-
workers[userKey] = mySegmentsUpdateWorker;
|
|
321
|
+
clients[userKey] = { hash64: hash64(userKey), worker: new MySegmentsUpdateWorker(mySegmentsSyncTask) };
|
|
220
322
|
|
|
221
323
|
const hash = hashUserKey(userKey);
|
|
222
324
|
|
|
@@ -239,6 +341,7 @@ export default function pushManagerFactory(
|
|
|
239
341
|
remove(userKey: string) {
|
|
240
342
|
const hash = hashUserKey(userKey);
|
|
241
343
|
delete userKeyHashes[hash];
|
|
344
|
+
delete clients[userKey];
|
|
242
345
|
}
|
|
243
346
|
}
|
|
244
347
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IMySegmentsUpdateData } from './SSEHandler/types';
|
|
1
|
+
import { IMySegmentsUpdateData, IMySegmentsUpdateV2Data, ISegmentUpdateData, ISplitUpdateData, ISplitKillData } from './SSEHandler/types';
|
|
2
2
|
import { ITask } from '../types';
|
|
3
3
|
import { IPollingManager, ISegmentsSyncTask } from '../polling/types';
|
|
4
4
|
import { IReadinessManager } from '../../readiness/types';
|
|
@@ -6,6 +6,7 @@ import { IFetchAuth } from '../../services/types';
|
|
|
6
6
|
import { IStorageSync } from '../../storages/types';
|
|
7
7
|
import { IEventEmitter, ISettings } from '../../types';
|
|
8
8
|
import { IPlatform } from '../../sdkFactory/types';
|
|
9
|
+
import { ControlType } from './constants';
|
|
9
10
|
|
|
10
11
|
// Internal SDK events, subscribed by SyncManager and PushManager
|
|
11
12
|
export type PUSH_SUBSYSTEM_UP = 'PUSH_SUBSYSTEM_UP'
|
|
@@ -15,6 +16,7 @@ export type PUSH_RETRYABLE_ERROR = 'PUSH_RETRYABLE_ERROR'
|
|
|
15
16
|
|
|
16
17
|
// Update-type push notifications, handled by NotificationProcessor
|
|
17
18
|
export type MY_SEGMENTS_UPDATE = 'MY_SEGMENTS_UPDATE';
|
|
19
|
+
export type MY_SEGMENTS_UPDATE_V2 = 'MY_SEGMENTS_UPDATE_V2';
|
|
18
20
|
export type SEGMENT_UPDATE = 'SEGMENT_UPDATE';
|
|
19
21
|
export type SPLIT_KILL = 'SPLIT_KILL';
|
|
20
22
|
export type SPLIT_UPDATE = 'SPLIT_UPDATE';
|
|
@@ -23,33 +25,23 @@ export type SPLIT_UPDATE = 'SPLIT_UPDATE';
|
|
|
23
25
|
export type CONTROL = 'CONTROL';
|
|
24
26
|
export type OCCUPANCY = 'OCCUPANCY';
|
|
25
27
|
|
|
26
|
-
export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MY_SEGMENTS_UPDATE | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL
|
|
28
|
+
export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MY_SEGMENTS_UPDATE | MY_SEGMENTS_UPDATE_V2 | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL | ControlType.STREAMING_RESET
|
|
29
|
+
|
|
30
|
+
type IParsedData<T extends IPushEvent> =
|
|
31
|
+
T extends MY_SEGMENTS_UPDATE ? IMySegmentsUpdateData :
|
|
32
|
+
T extends MY_SEGMENTS_UPDATE_V2 ? IMySegmentsUpdateV2Data :
|
|
33
|
+
T extends SEGMENT_UPDATE ? ISegmentUpdateData :
|
|
34
|
+
T extends SPLIT_UPDATE ? ISplitUpdateData :
|
|
35
|
+
T extends SPLIT_KILL ? ISplitKillData : undefined;
|
|
27
36
|
|
|
28
37
|
/**
|
|
29
38
|
* EventEmitter used as Feedback Loop between the SyncManager and PushManager,
|
|
30
39
|
* where the latter pushes messages and the former consumes it
|
|
31
40
|
*/
|
|
32
41
|
export interface IPushEventEmitter extends IEventEmitter {
|
|
33
|
-
once<T extends IPushEvent>(event: T, listener: (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
T extends SPLIT_UPDATE ? [changeNumber: number] :
|
|
37
|
-
T extends SPLIT_KILL ? [changeNumber: number, splitName: string, defaultTreatment: string] :
|
|
38
|
-
any[]) => void): this;
|
|
39
|
-
|
|
40
|
-
on<T extends IPushEvent>(event: T, listener: (...args:
|
|
41
|
-
T extends MY_SEGMENTS_UPDATE ? [parsedData: IMySegmentsUpdateData, channel: string] :
|
|
42
|
-
T extends SEGMENT_UPDATE ? [changeNumber: number, segmentName: string] :
|
|
43
|
-
T extends SPLIT_UPDATE ? [changeNumber: number] :
|
|
44
|
-
T extends SPLIT_KILL ? [changeNumber: number, splitName: string, defaultTreatment: string] :
|
|
45
|
-
any[]) => void): this;
|
|
46
|
-
|
|
47
|
-
emit<T extends IPushEvent>(event: T, ...args:
|
|
48
|
-
T extends MY_SEGMENTS_UPDATE ? [parsedData: IMySegmentsUpdateData, channel: string] :
|
|
49
|
-
T extends SEGMENT_UPDATE ? [changeNumber: number, segmentName: string] :
|
|
50
|
-
T extends SPLIT_UPDATE ? [changeNumber: number] :
|
|
51
|
-
T extends SPLIT_KILL ? [changeNumber: number, splitName: string, defaultTreatment: string] :
|
|
52
|
-
any[]): boolean;
|
|
42
|
+
once<T extends IPushEvent>(event: T, listener: (parsedData: IParsedData<T>, channel: T extends MY_SEGMENTS_UPDATE ? string : undefined) => void): this;
|
|
43
|
+
on<T extends IPushEvent>(event: T, listener: (parsedData: IParsedData<T>, channel: T extends MY_SEGMENTS_UPDATE ? string : undefined) => void): this;
|
|
44
|
+
emit<T extends IPushEvent>(event: T, parsedData?: IParsedData<T>, channel?: T extends MY_SEGMENTS_UPDATE ? string : undefined): boolean;
|
|
53
45
|
}
|
|
54
46
|
|
|
55
47
|
/**
|
|
@@ -45,5 +45,5 @@ export function latenciesSyncTaskFactory(
|
|
|
45
45
|
): ISyncTask {
|
|
46
46
|
|
|
47
47
|
// don't retry metrics.
|
|
48
|
-
return submitterSyncTaskFactory(log, postMetricsLatencies, latenciesCache, metricsRefreshRate, 'latency metrics', latencyTracker, fromCache<number[]>('latencies'));
|
|
48
|
+
return submitterSyncTaskFactory(log, postMetricsLatencies, latenciesCache, metricsRefreshRate, 'latency metrics', latencyTracker, fromCache<number[]>('latencies'), 0, true);
|
|
49
49
|
}
|
|
@@ -17,6 +17,7 @@ export function submitterSyncTaskFactory<TState extends { length?: number }>(
|
|
|
17
17
|
latencyTracker?: ITimeTracker,
|
|
18
18
|
fromCacheToPayload?: (cacheData: TState) => any,
|
|
19
19
|
maxRetries: number = 0,
|
|
20
|
+
debugLogs?: boolean
|
|
20
21
|
): ISyncTask<[], void> {
|
|
21
22
|
|
|
22
23
|
let retries = 0;
|
|
@@ -27,7 +28,7 @@ export function submitterSyncTaskFactory<TState extends { length?: number }>(
|
|
|
27
28
|
const data = sourceCache.state();
|
|
28
29
|
|
|
29
30
|
const dataCount: number | '' = typeof data.length === 'number' ? data.length : '';
|
|
30
|
-
log
|
|
31
|
+
log[debugLogs ? 'debug' : 'info'](SUBMITTERS_PUSH, [dataCount, dataName]);
|
|
31
32
|
const latencyTrackerStop = latencyTracker && latencyTracker.start();
|
|
32
33
|
|
|
33
34
|
const jsonPayload = JSON.stringify(fromCacheToPayload ? fromCacheToPayload(data) : data);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { splitsParserFromFileFactory } from './offline/splitsParser/splitsParserFromFile';
|
|
2
|
+
import { syncManagerOfflineFactory } from './syncManagerOffline';
|
|
3
|
+
import { SplitIO } from '../types';
|
|
4
|
+
import { LOCALHOST_MODE } from '../utils/constants';
|
|
5
|
+
|
|
6
|
+
// Factory of Localhost SyncManager based on yaml file.
|
|
7
|
+
// Requires Node 'fs' and 'path' APIs.
|
|
8
|
+
export function LocalhostFromFile(): SplitIO.LocalhostFactory {
|
|
9
|
+
const localhost = syncManagerOfflineFactory(splitsParserFromFileFactory);
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
localhost.type = LOCALHOST_MODE;
|
|
12
|
+
return localhost;
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { splitsParserFromSettingsFactory } from './offline/splitsParser/splitsParserFromSettings';
|
|
2
|
+
import { syncManagerOfflineFactory } from './syncManagerOffline';
|
|
3
|
+
import { SplitIO } from '../types';
|
|
4
|
+
import { LOCALHOST_MODE } from '../utils/constants';
|
|
5
|
+
|
|
6
|
+
// Factory of Localhost SyncManager based on JS object.
|
|
7
|
+
export function LocalhostFromObject(): SplitIO.LocalhostFactory {
|
|
8
|
+
const localhost = syncManagerOfflineFactory(splitsParserFromSettingsFactory);
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
localhost.type = LOCALHOST_MODE;
|
|
11
|
+
return localhost;
|
|
12
|
+
}
|
|
@@ -16,7 +16,7 @@ function flush() {
|
|
|
16
16
|
* @param splitsParser e.g., `splitsParserFromFile`, `splitsParserFromSettings`.
|
|
17
17
|
*/
|
|
18
18
|
export function syncManagerOfflineFactory(
|
|
19
|
-
|
|
19
|
+
splitsParserFactory: () => ISplitsParser
|
|
20
20
|
): (params: ISyncManagerFactoryParams) => ISyncManagerCS {
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -29,7 +29,7 @@ export function syncManagerOfflineFactory(
|
|
|
29
29
|
}: ISyncManagerFactoryParams): ISyncManagerCS {
|
|
30
30
|
|
|
31
31
|
return objectAssign(
|
|
32
|
-
fromObjectSyncTaskFactory(
|
|
32
|
+
fromObjectSyncTaskFactory(splitsParserFactory(), storage, readiness, settings),
|
|
33
33
|
{
|
|
34
34
|
// fake flush, that resolves immediately
|
|
35
35
|
flush,
|
|
@@ -38,7 +38,7 @@ export function syncManagerOfflineFactory(
|
|
|
38
38
|
shared(matchingKey: string, readinessManager: IReadinessManager): ISyncManager {
|
|
39
39
|
return {
|
|
40
40
|
start() {
|
|
41
|
-
// In LOCALHOST mode, shared clients are ready in the next event cycle than created
|
|
41
|
+
// In LOCALHOST mode, shared clients are ready in the next event-loop cycle than created
|
|
42
42
|
// SDK_READY cannot be emitted directly because this will not update the readiness status
|
|
43
43
|
setTimeout(() => {
|
|
44
44
|
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); // SDK_SPLITS_ARRIVED emitted by main SyncManager
|
|
@@ -75,17 +75,28 @@ export function syncManagerOnlineFactory(
|
|
|
75
75
|
pollingManager.syncAll();
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
if (pushManager) {
|
|
79
|
+
pushManager.on(PUSH_SUBSYSTEM_UP, stopPollingAndSyncAll);
|
|
80
|
+
pushManager.on(PUSH_SUBSYSTEM_DOWN, startPolling);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let running = false; // flag that indicates whether the syncManager has been started (true) or stopped (false)
|
|
84
|
+
let startFirstTime = true; // flag to distinguish calling the `start` method for the first time, to support pausing and resuming the synchronization
|
|
79
85
|
|
|
80
86
|
return {
|
|
87
|
+
pushManager,
|
|
81
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Method used to start the syncManager for the first time, or resume it after being stopped.
|
|
91
|
+
*/
|
|
82
92
|
start() {
|
|
83
93
|
// start syncing splits and segments
|
|
84
94
|
if (pushManager) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
// Doesn't call `syncAll` when the syncManager is resuming
|
|
96
|
+
if (startFirstTime) {
|
|
97
|
+
pollingManager.syncAll();
|
|
98
|
+
startFirstTime = false;
|
|
99
|
+
}
|
|
89
100
|
pushManager.start();
|
|
90
101
|
} else {
|
|
91
102
|
pollingManager.start();
|
|
@@ -96,6 +107,9 @@ export function syncManagerOnlineFactory(
|
|
|
96
107
|
running = true;
|
|
97
108
|
},
|
|
98
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Method used to stop/pause the syncManager.
|
|
112
|
+
*/
|
|
99
113
|
stop() {
|
|
100
114
|
// stop syncing
|
|
101
115
|
if (pushManager) pushManager.stop();
|
package/src/sync/syncTask.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { ILogger } from '../logger/types';
|
|
|
3
3
|
import { ISyncTask } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Creates a syncTask that handles the periodic execution of a
|
|
6
|
+
* Creates a syncTask that handles the periodic execution of a given task ("start" and "stop" methods).
|
|
7
7
|
* The task can be executed once calling the "execute" method.
|
|
8
8
|
* NOTE: Multiple calls to "execute" are not queued. Use "isExecuting" method to handle synchronization.
|
|
9
9
|
*
|
package/src/sync/types.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { IPlatform } from '../sdkFactory/types';
|
|
|
3
3
|
import { ISplitApi } from '../services/types';
|
|
4
4
|
import { IStorageSync } from '../storages/types';
|
|
5
5
|
import { ISettings } from '../types';
|
|
6
|
+
import { IPushManager } from './streaming/types';
|
|
6
7
|
|
|
7
8
|
export interface ITask<Input extends any[] = []> {
|
|
8
9
|
/**
|
|
@@ -41,7 +42,8 @@ export interface ITimeTracker {
|
|
|
41
42
|
/** SyncManager */
|
|
42
43
|
|
|
43
44
|
export interface ISyncManager extends ITask {
|
|
44
|
-
flush(): Promise<any
|
|
45
|
+
flush(): Promise<any>,
|
|
46
|
+
pushManager?: IPushManager
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
export interface ISyncManagerCS extends ISyncManager {
|
|
@@ -2,18 +2,16 @@ import { ImpressionDTO } from '../../types';
|
|
|
2
2
|
import LRUCache from '../../utils/LRUCache';
|
|
3
3
|
import { IImpressionObserver } from './types';
|
|
4
4
|
|
|
5
|
-
export default class ImpressionObserver implements IImpressionObserver {
|
|
6
|
-
private cache: LRUCache<
|
|
7
|
-
private hasher: (impression: ImpressionDTO) =>
|
|
5
|
+
export default class ImpressionObserver<K extends string | number> implements IImpressionObserver {
|
|
6
|
+
private cache: LRUCache<K, number>;
|
|
7
|
+
private hasher: (impression: ImpressionDTO) => K;
|
|
8
8
|
|
|
9
|
-
constructor(size: number, hasher: (impression: ImpressionDTO) =>
|
|
9
|
+
constructor(size: number, hasher: (impression: ImpressionDTO) => K) {
|
|
10
10
|
this.cache = new LRUCache(size);
|
|
11
11
|
this.hasher = hasher;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
testAndSet(impression: ImpressionDTO) {
|
|
15
|
-
if (!impression) return;
|
|
16
|
-
|
|
17
15
|
const hash = this.hasher(impression);
|
|
18
16
|
const previous = this.cache.get(hash);
|
|
19
17
|
this.cache.set(hash, impression.time);
|
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
import { ImpressionDTO } from '../../types';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function _unknownIfNull(s: any) {
|
|
6
|
-
return s ? s : UNKNOWN;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function _zeroIfNull(l: any) {
|
|
10
|
-
return l ? l : 0;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default function buildKey(impression: ImpressionDTO) {
|
|
14
|
-
return `${_unknownIfNull(impression.keyName)}
|
|
15
|
-
:${_unknownIfNull(impression.feature)}
|
|
16
|
-
:${_unknownIfNull(impression.treatment)}
|
|
17
|
-
:${_unknownIfNull(impression.label)}
|
|
18
|
-
:${_zeroIfNull(impression.changeNumber)}`;
|
|
3
|
+
export function buildKey(impression: ImpressionDTO) {
|
|
4
|
+
return `${impression.keyName}:${impression.feature}:${impression.treatment}:${impression.label}:${impression.changeNumber}`;
|
|
19
5
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import ImpressionObserver from './ImpressionObserver';
|
|
2
2
|
import { hash } from '../../utils/murmur3/murmur3';
|
|
3
|
-
import buildKey from './buildKey';
|
|
3
|
+
import { buildKey } from './buildKey';
|
|
4
4
|
import { ImpressionDTO } from '../../types';
|
|
5
5
|
|
|
6
6
|
export function hashImpression32(impression: ImpressionDTO) {
|
|
7
|
-
return
|
|
7
|
+
return hash(buildKey(impression));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const LAST_SEEN_CACHE_SIZE = 500; // cache up to 500 impression hashes
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import ImpressionObserver from './ImpressionObserver';
|
|
2
|
-
import { hash128 } from '../../utils/murmur3/
|
|
3
|
-
import buildKey from './buildKey';
|
|
2
|
+
import { hash128 } from '../../utils/murmur3/murmur3_128_x86';
|
|
3
|
+
import { buildKey } from './buildKey';
|
|
4
4
|
import { ImpressionDTO } from '../../types';
|
|
5
5
|
|
|
6
6
|
export function hashImpression128(impression: ImpressionDTO) {
|
|
7
|
-
return
|
|
7
|
+
return hash128(buildKey(impression));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const LAST_SEEN_CACHE_SIZE = 500000; // cache up to 500k impression hashes
|