@splitsoftware/splitio-commons 1.5.0 → 1.5.1-rc.2
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/CHANGES.txt +5 -0
- package/cjs/listeners/browser.js +35 -15
- package/cjs/services/splitApi.js +4 -4
- package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +5 -5
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +2 -2
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +34 -34
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +4 -3
- package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +46 -46
- package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +82 -64
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +74 -58
- package/cjs/sync/streaming/UpdateWorkers/constants.js +6 -0
- package/cjs/sync/streaming/pushManager.js +6 -7
- package/cjs/sync/syncTask.js +13 -16
- package/cjs/utils/Backoff.js +3 -2
- package/esm/listeners/browser.js +35 -15
- package/esm/services/splitApi.js +4 -4
- package/esm/sync/polling/fetchers/segmentChangesFetcher.js +5 -5
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +2 -2
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +34 -34
- package/esm/sync/polling/updaters/splitChangesUpdater.js +4 -3
- package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +46 -47
- package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +82 -65
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +74 -59
- package/esm/sync/streaming/UpdateWorkers/constants.js +3 -0
- package/esm/sync/streaming/pushManager.js +6 -7
- package/esm/sync/syncTask.js +13 -16
- package/esm/utils/Backoff.js +3 -2
- package/package.json +1 -5
- package/src/listeners/browser.ts +34 -14
- package/src/logger/.DS_Store +0 -0
- package/src/services/splitApi.ts +4 -4
- package/src/services/types.ts +2 -2
- package/src/sync/polling/fetchers/segmentChangesFetcher.ts +5 -4
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +2 -1
- package/src/sync/polling/fetchers/types.ts +2 -0
- package/src/sync/polling/pollingManagerCS.ts +5 -5
- package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +2 -2
- package/src/sync/polling/types.ts +14 -6
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +4 -4
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +34 -32
- package/src/sync/polling/updaters/splitChangesUpdater.ts +5 -4
- package/src/sync/streaming/SSEHandler/types.ts +0 -7
- package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +45 -54
- package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +78 -63
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +73 -61
- package/src/sync/streaming/UpdateWorkers/constants.ts +3 -0
- package/src/sync/streaming/UpdateWorkers/types.ts +2 -4
- package/src/sync/streaming/pushManager.ts +12 -12
- package/src/sync/streaming/types.ts +2 -2
- package/src/sync/syncTask.ts +16 -18
- package/src/utils/Backoff.ts +7 -2
- package/types/integrations/ga/GaToSplitPlugin.d.ts +3 -0
- package/types/integrations/ga/SplitToGaPlugin.d.ts +4 -0
- package/types/integrations/ga/autoRequire.d.ts +4 -0
- package/types/listeners/browser.d.ts +6 -6
- package/types/logger/browser/{DebugLogger.d.ts → debugLogger.d.ts} +0 -0
- package/types/logger/browser/{ErrorLogger.d.ts → errorLogger.d.ts} +0 -0
- package/types/logger/browser/{InfoLogger.d.ts → infoLogger.d.ts} +0 -0
- package/types/logger/browser/{WarnLogger.d.ts → warnLogger.d.ts} +0 -0
- package/types/logger/codes.d.ts +2 -0
- package/types/logger/codesConstants.d.ts +117 -0
- package/types/logger/codesConstantsBrowser.d.ts +2 -0
- package/types/logger/codesConstantsNode.d.ts +14 -0
- package/types/logger/codesDebug.d.ts +1 -0
- package/types/logger/codesDebugBrowser.d.ts +1 -0
- package/types/logger/codesDebugNode.d.ts +1 -0
- package/types/logger/codesError.d.ts +1 -0
- package/types/logger/codesErrorNode.d.ts +1 -0
- package/types/logger/codesInfo.d.ts +1 -0
- package/types/logger/codesWarn.d.ts +1 -0
- package/types/logger/codesWarnNode.d.ts +1 -0
- package/types/logger/debugLogger.d.ts +2 -0
- package/types/logger/errorLogger.d.ts +2 -0
- package/types/logger/infoLogger.d.ts +2 -0
- package/types/logger/messages/debugBrowser.d.ts +1 -0
- package/types/logger/messages/debugNode.d.ts +1 -0
- package/types/logger/messages/errorNode.d.ts +1 -0
- package/types/logger/messages/warnNode.d.ts +1 -0
- package/types/logger/noopLogger.d.ts +2 -0
- package/types/logger/warnLogger.d.ts +2 -0
- package/types/sdkFactory/userConsentProps.d.ts +6 -0
- package/types/sdkManager/sdkManagerMethod.d.ts +6 -0
- package/types/services/types.d.ts +2 -2
- package/types/storages/getRegisteredSegments.d.ts +10 -0
- package/types/storages/inMemory/index.d.ts +10 -0
- package/types/storages/parseSegments.d.ts +6 -0
- package/types/sync/polling/fetchers/types.d.ts +2 -2
- package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +2 -2
- package/types/sync/polling/syncTasks/splitsSyncTask.copy.d.ts +35 -0
- package/types/sync/polling/syncTasks/splitsSyncTask.morelikeoriginal.d.ts +35 -0
- package/types/sync/polling/types.d.ts +11 -6
- package/types/sync/polling/updaters/segmentChangesUpdater.d.ts +1 -1
- package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -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/SSEHandler/types.d.ts +0 -4
- package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +3 -24
- package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +3 -23
- package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +6 -33
- package/types/sync/streaming/UpdateWorkers/constants.d.ts +3 -0
- package/types/sync/streaming/UpdateWorkers/types.d.ts +1 -2
- package/types/sync/streaming/pushManagerCS.d.ts +1 -0
- package/types/sync/streaming/pushManagerNoUsers.d.ts +13 -0
- package/types/sync/streaming/pushManagerSS.d.ts +1 -0
- package/types/sync/streaming/types.d.ts +2 -2
- package/types/sync/submitters/telemetrySyncTask.d.ts +0 -0
- package/types/sync/syncManagerFromFile.d.ts +2 -0
- package/types/sync/syncManagerFromObject.d.ts +2 -0
- package/types/sync/syncManagerOffline.d.ts +9 -0
- package/types/sync/syncTask.d.ts +2 -3
- package/types/trackers/telemetryRecorder.d.ts +0 -0
- package/types/utils/Backoff.d.ts +2 -0
- package/types/utils/EventEmitter.d.ts +4 -0
- package/types/utils/consent.d.ts +2 -0
- package/types/utils/lang/errors.d.ts +10 -0
- package/types/utils/murmur3/commons.d.ts +12 -0
- package/types/utils/settingsValidation/buildMetadata.d.ts +3 -0
- package/types/utils/settingsValidation/localhost/index.d.ts +9 -0
- package/types/utils/settingsValidation/logger.d.ts +11 -0
- package/types/utils/settingsValidation/runtime/browser.d.ts +2 -0
- package/types/utils/settingsValidation/runtime/node.d.ts +2 -0
- package/types/utils/settingsValidation/userConsent.d.ts +5 -0
- package/types/utils/timeTracker/index.d.ts +1 -70
- package/cjs/sync/offline/LocalhostFromFile.js +0 -13
- package/cjs/sync/offline/splitsParser/splitsParserFromFile.js +0 -151
- package/esm/sync/offline/LocalhostFromFile.js +0 -9
- package/esm/sync/offline/splitsParser/splitsParserFromFile.js +0 -146
- package/src/sync/offline/LocalhostFromFile.ts +0 -12
- package/src/sync/offline/splitsParser/splitsParserFromFile.ts +0 -182
|
@@ -1,84 +1,99 @@
|
|
|
1
|
+
import { ILogger } from '../../../logger/types';
|
|
1
2
|
import { ISegmentsCacheSync } from '../../../storages/types';
|
|
2
3
|
import { Backoff } from '../../../utils/Backoff';
|
|
3
4
|
import { ISegmentsSyncTask } from '../../polling/types';
|
|
4
5
|
import { ISegmentUpdateData } from '../SSEHandler/types';
|
|
6
|
+
import { FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_RETRIES, FETCH_BACKOFF_MAX_WAIT } from './constants';
|
|
5
7
|
import { IUpdateWorker } from './types';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
|
-
*
|
|
10
|
+
* SegmentsUpdateWorker factory
|
|
9
11
|
*/
|
|
10
|
-
export
|
|
12
|
+
export function SegmentsUpdateWorker(log: ILogger, segmentsSyncTask: ISegmentsSyncTask, segmentsCache: ISegmentsCacheSync): IUpdateWorker {
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
// Handles retries with CDN bypass per segment name
|
|
15
|
+
function SegmentUpdateWorker(segment: string) {
|
|
16
|
+
let maxChangeNumber = 0;
|
|
17
|
+
let handleNewEvent = false;
|
|
18
|
+
let isHandlingEvent: boolean;
|
|
19
|
+
let cdnBypass: boolean;
|
|
20
|
+
const backoff = new Backoff(__handleSegmentUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
constructor(segmentsSyncTask: ISegmentsSyncTask, segmentsCache: ISegmentsCacheSync) {
|
|
23
|
-
this.segmentsCache = segmentsCache;
|
|
24
|
-
this.segmentsSyncTask = segmentsSyncTask;
|
|
25
|
-
this.maxChangeNumbers = {};
|
|
26
|
-
this.handleNewEvent = false;
|
|
27
|
-
this.put = this.put.bind(this);
|
|
28
|
-
this.__handleSegmentUpdateCall = this.__handleSegmentUpdateCall.bind(this);
|
|
29
|
-
this.backoff = new Backoff(this.__handleSegmentUpdateCall);
|
|
30
|
-
}
|
|
22
|
+
function __handleSegmentUpdateCall() {
|
|
23
|
+
isHandlingEvent = true;
|
|
24
|
+
if (maxChangeNumber > segmentsCache.getChangeNumber(segment)) {
|
|
25
|
+
handleNewEvent = false;
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (segmentsToFetch.length > 0) {
|
|
40
|
-
this.handleNewEvent = false;
|
|
41
|
-
const currentMaxChangeNumbers = segmentsToFetch.map(segmentToFetch => this.maxChangeNumbers[segmentToFetch]);
|
|
27
|
+
// fetch segments revalidating data if cached
|
|
28
|
+
segmentsSyncTask.execute(false, segment, true, cdnBypass ? maxChangeNumber : undefined).then(() => {
|
|
29
|
+
if (!isHandlingEvent) return; // halt if `stop` has been called
|
|
30
|
+
if (handleNewEvent) {
|
|
31
|
+
__handleSegmentUpdateCall();
|
|
32
|
+
} else {
|
|
33
|
+
const attempts = backoff.attempts + 1;
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
segmentsToFetch.forEach((fetchedSegment, index) => {
|
|
49
|
-
if (this.maxChangeNumbers[fetchedSegment] === currentMaxChangeNumbers[index]) this.maxChangeNumbers[fetchedSegment] = -1;
|
|
50
|
-
});
|
|
51
|
-
} else {
|
|
52
|
-
// recursive invocation with backoff if there was some error
|
|
53
|
-
this.backoff.scheduleCall();
|
|
54
|
-
}
|
|
35
|
+
if (maxChangeNumber <= segmentsCache.getChangeNumber(segment)) {
|
|
36
|
+
log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
|
|
37
|
+
isHandlingEvent = false;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
55
40
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
41
|
+
if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
|
|
42
|
+
backoff.scheduleCall();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
63
45
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
46
|
+
if (cdnBypass) {
|
|
47
|
+
log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
|
|
48
|
+
isHandlingEvent = false;
|
|
49
|
+
} else {
|
|
50
|
+
backoff.reset();
|
|
51
|
+
cdnBypass = true;
|
|
52
|
+
__handleSegmentUpdateCall();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
isHandlingEvent = false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
72
60
|
|
|
73
|
-
|
|
61
|
+
return {
|
|
62
|
+
put(changeNumber: number) {
|
|
63
|
+
const currentChangeNumber = segmentsCache.getChangeNumber(segment);
|
|
74
64
|
|
|
75
|
-
|
|
76
|
-
this.handleNewEvent = true;
|
|
77
|
-
this.backoff.reset();
|
|
65
|
+
if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
|
|
78
66
|
|
|
79
|
-
|
|
67
|
+
maxChangeNumber = changeNumber;
|
|
68
|
+
handleNewEvent = true;
|
|
69
|
+
cdnBypass = false;
|
|
80
70
|
|
|
81
|
-
|
|
71
|
+
if (backoff.timeoutID || !isHandlingEvent) __handleSegmentUpdateCall();
|
|
72
|
+
backoff.reset();
|
|
73
|
+
},
|
|
74
|
+
stop() {
|
|
75
|
+
isHandlingEvent = false;
|
|
76
|
+
backoff.reset();
|
|
77
|
+
}
|
|
78
|
+
};
|
|
82
79
|
}
|
|
83
80
|
|
|
81
|
+
const segments: Record<string, ReturnType<typeof SegmentUpdateWorker>> = {};
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
/**
|
|
85
|
+
* Invoked by NotificationProcessor on SEGMENT_UPDATE event
|
|
86
|
+
*
|
|
87
|
+
* @param {number} changeNumber change number of the SEGMENT_UPDATE notification
|
|
88
|
+
* @param {string} segmentName segment name of the SEGMENT_UPDATE notification
|
|
89
|
+
*/
|
|
90
|
+
put({ changeNumber, segmentName }: ISegmentUpdateData) {
|
|
91
|
+
if (!segments[segmentName]) segments[segmentName] = SegmentUpdateWorker(segmentName);
|
|
92
|
+
segments[segmentName].put(changeNumber);
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
stop() {
|
|
96
|
+
Object.keys(segments).forEach(segmentName => segments[segmentName].stop());
|
|
97
|
+
}
|
|
98
|
+
};
|
|
84
99
|
}
|
|
@@ -1,58 +1,63 @@
|
|
|
1
|
+
import { ILogger } from '../../../logger/types';
|
|
1
2
|
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
2
3
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
3
4
|
import { ISplitsCacheSync } from '../../../storages/types';
|
|
4
5
|
import { Backoff } from '../../../utils/Backoff';
|
|
5
6
|
import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
|
|
6
7
|
import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
|
|
8
|
+
import { FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT, FETCH_BACKOFF_MAX_RETRIES } from './constants';
|
|
7
9
|
import { IUpdateWorker } from './types';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
|
-
* SplitsUpdateWorker
|
|
12
|
+
* SplitsUpdateWorker factory
|
|
11
13
|
*/
|
|
12
|
-
export
|
|
14
|
+
export function SplitsUpdateWorker(log: ILogger, splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker & { killSplit(event: ISplitKillData): void } {
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
private handleNewEvent: boolean;
|
|
20
|
-
readonly backoff: Backoff;
|
|
16
|
+
let maxChangeNumber = 0;
|
|
17
|
+
let handleNewEvent = false;
|
|
18
|
+
let isHandlingEvent: boolean;
|
|
19
|
+
let cdnBypass: boolean;
|
|
20
|
+
const backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*/
|
|
27
|
-
constructor(splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, segmentsSyncTask?: ISegmentsSyncTask) {
|
|
28
|
-
this.splitsCache = splitsCache;
|
|
29
|
-
this.splitsSyncTask = splitsSyncTask;
|
|
30
|
-
this.splitsEventEmitter = splitsEventEmitter;
|
|
31
|
-
this.segmentsSyncTask = segmentsSyncTask;
|
|
32
|
-
this.maxChangeNumber = 0;
|
|
33
|
-
this.handleNewEvent = false;
|
|
34
|
-
this.put = this.put.bind(this);
|
|
35
|
-
this.killSplit = this.killSplit.bind(this);
|
|
36
|
-
this.__handleSplitUpdateCall = this.__handleSplitUpdateCall.bind(this);
|
|
37
|
-
this.backoff = new Backoff(this.__handleSplitUpdateCall);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Private method
|
|
41
|
-
// Preconditions: this.splitsSyncTask.isSynchronizingSplits === false
|
|
42
|
-
__handleSplitUpdateCall() {
|
|
43
|
-
if (this.maxChangeNumber > this.splitsCache.getChangeNumber()) {
|
|
44
|
-
this.handleNewEvent = false;
|
|
22
|
+
function __handleSplitUpdateCall() {
|
|
23
|
+
isHandlingEvent = true;
|
|
24
|
+
if (maxChangeNumber > splitsCache.getChangeNumber()) {
|
|
25
|
+
handleNewEvent = false;
|
|
45
26
|
|
|
46
27
|
// fetch splits revalidating data if cached
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
28
|
+
splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined).then(() => {
|
|
29
|
+
if (!isHandlingEvent) return; // halt if `stop` has been called
|
|
30
|
+
if (handleNewEvent) {
|
|
31
|
+
__handleSplitUpdateCall();
|
|
50
32
|
} else {
|
|
51
33
|
// fetch new registered segments for server-side API. Not retrying on error
|
|
52
|
-
if (
|
|
53
|
-
|
|
34
|
+
if (segmentsSyncTask) segmentsSyncTask.execute(true);
|
|
35
|
+
|
|
36
|
+
const attempts = backoff.attempts + 1;
|
|
37
|
+
|
|
38
|
+
if (maxChangeNumber <= splitsCache.getChangeNumber()) {
|
|
39
|
+
log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
|
|
40
|
+
isHandlingEvent = false;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
|
|
45
|
+
backoff.scheduleCall();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (cdnBypass) {
|
|
50
|
+
log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
|
|
51
|
+
isHandlingEvent = false;
|
|
52
|
+
} else {
|
|
53
|
+
backoff.reset();
|
|
54
|
+
cdnBypass = true;
|
|
55
|
+
__handleSplitUpdateCall();
|
|
56
|
+
}
|
|
54
57
|
}
|
|
55
58
|
});
|
|
59
|
+
} else {
|
|
60
|
+
isHandlingEvent = false;
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
@@ -61,34 +66,41 @@ export class SplitsUpdateWorker implements IUpdateWorker {
|
|
|
61
66
|
*
|
|
62
67
|
* @param {number} changeNumber change number of the SPLIT_UPDATE notification
|
|
63
68
|
*/
|
|
64
|
-
put({ changeNumber }: Pick<ISplitUpdateData, 'changeNumber'>) {
|
|
65
|
-
const currentChangeNumber =
|
|
66
|
-
|
|
67
|
-
if (changeNumber <= currentChangeNumber || changeNumber <= this.maxChangeNumber) return;
|
|
69
|
+
function put({ changeNumber }: Pick<ISplitUpdateData, 'changeNumber'>) {
|
|
70
|
+
const currentChangeNumber = splitsCache.getChangeNumber();
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
this.handleNewEvent = true;
|
|
71
|
-
this.backoff.reset();
|
|
72
|
+
if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
maxChangeNumber = changeNumber;
|
|
75
|
+
handleNewEvent = true;
|
|
76
|
+
cdnBypass = false;
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
if (backoff.timeoutID || !isHandlingEvent) __handleSplitUpdateCall();
|
|
79
|
+
backoff.reset();
|
|
76
80
|
}
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
* @param {number} changeNumber change number of the SPLIT_UPDATE notification
|
|
82
|
-
* @param {string} splitName name of split to kill
|
|
83
|
-
* @param {string} defaultTreatment default treatment value
|
|
84
|
-
*/
|
|
85
|
-
killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
|
|
86
|
-
if (this.splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
|
|
87
|
-
// trigger an SDK_UPDATE if Split was killed locally
|
|
88
|
-
this.splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
|
|
89
|
-
}
|
|
90
|
-
// queues the SplitChanges fetch (only if changeNumber is newer)
|
|
91
|
-
this.put({ changeNumber });
|
|
92
|
-
}
|
|
82
|
+
return {
|
|
83
|
+
put,
|
|
93
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Invoked by NotificationProcessor on SPLIT_KILL event
|
|
87
|
+
*
|
|
88
|
+
* @param {number} changeNumber change number of the SPLIT_UPDATE notification
|
|
89
|
+
* @param {string} splitName name of split to kill
|
|
90
|
+
* @param {string} defaultTreatment default treatment value
|
|
91
|
+
*/
|
|
92
|
+
killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
|
|
93
|
+
if (splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
|
|
94
|
+
// trigger an SDK_UPDATE if Split was killed locally
|
|
95
|
+
splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
|
|
96
|
+
}
|
|
97
|
+
// queues the SplitChanges fetch (only if changeNumber is newer)
|
|
98
|
+
put({ changeNumber });
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
stop() {
|
|
102
|
+
isHandlingEvent = false;
|
|
103
|
+
backoff.reset();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
94
106
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IPushEventEmitter, IPushManager } from './types';
|
|
2
2
|
import { ISSEClient } from './SSEClient/types';
|
|
3
|
-
import {
|
|
3
|
+
import { IMySegmentsSyncTask, IPollingManager, ISegmentsSyncTask } from '../polling/types';
|
|
4
4
|
import { objectAssign } from '../../utils/lang/objectAssign';
|
|
5
5
|
import { Backoff } from '../../utils/Backoff';
|
|
6
6
|
import { SSEHandlerFactory } from './SSEHandler';
|
|
@@ -20,6 +20,7 @@ import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64';
|
|
|
20
20
|
import { IAuthTokenPushEnabled } from './AuthClient/types';
|
|
21
21
|
import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants';
|
|
22
22
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
23
|
+
import { IUpdateWorker } from './UpdateWorkers/types';
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* PushManager factory:
|
|
@@ -55,15 +56,15 @@ export function pushManagerFactory(
|
|
|
55
56
|
|
|
56
57
|
// init workers
|
|
57
58
|
// MySegmentsUpdateWorker (client-side) are initiated in `add` method
|
|
58
|
-
const segmentsUpdateWorker = userKey ? undefined :
|
|
59
|
+
const segmentsUpdateWorker = userKey ? undefined : SegmentsUpdateWorker(log, pollingManager.segmentsSyncTask as ISegmentsSyncTask, storage.segments);
|
|
59
60
|
// For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
|
|
60
|
-
const splitsUpdateWorker =
|
|
61
|
+
const splitsUpdateWorker = SplitsUpdateWorker(log, storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask as ISegmentsSyncTask);
|
|
61
62
|
|
|
62
63
|
// [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
|
|
63
64
|
const userKeyHashes: Record<string, string> = {};
|
|
64
65
|
// [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
|
|
65
66
|
// Hash64 is used to process MY_SEGMENTS_UPDATE_V2 events and dispatch actions to the corresponding MySegmentsUpdateWorker.
|
|
66
|
-
const clients: Record<string, { hash64: Hash64, worker:
|
|
67
|
+
const clients: Record<string, { hash64: Hash64, worker: IUpdateWorker }> = {};
|
|
67
68
|
|
|
68
69
|
// [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
|
|
69
70
|
let connectForNewClient = false;
|
|
@@ -169,9 +170,9 @@ export function pushManagerFactory(
|
|
|
169
170
|
|
|
170
171
|
// cancel scheduled fetch retries of Splits, Segments, and MySegments Update Workers
|
|
171
172
|
function stopWorkers() {
|
|
172
|
-
splitsUpdateWorker.
|
|
173
|
-
if (userKey) forOwn(clients, ({ worker }) => worker.
|
|
174
|
-
else
|
|
173
|
+
splitsUpdateWorker.stop();
|
|
174
|
+
if (userKey) forOwn(clients, ({ worker }) => worker.stop());
|
|
175
|
+
else segmentsUpdateWorker!.stop();
|
|
175
176
|
}
|
|
176
177
|
|
|
177
178
|
pushEmitter.on(PUSH_SUBSYSTEM_DOWN, stopWorkers);
|
|
@@ -180,7 +181,6 @@ export function pushManagerFactory(
|
|
|
180
181
|
// Otherwise it is unnecessary (e.g, STREAMING_RESUMED).
|
|
181
182
|
pushEmitter.on(PUSH_SUBSYSTEM_UP, () => {
|
|
182
183
|
connectPushRetryBackoff.reset();
|
|
183
|
-
stopWorkers();
|
|
184
184
|
});
|
|
185
185
|
|
|
186
186
|
/** Fallback to polling without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */
|
|
@@ -294,7 +294,7 @@ export function pushManagerFactory(
|
|
|
294
294
|
});
|
|
295
295
|
});
|
|
296
296
|
} else {
|
|
297
|
-
pushEmitter.on(SEGMENT_UPDATE,
|
|
297
|
+
pushEmitter.on(SEGMENT_UPDATE, segmentsUpdateWorker!.put);
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
return objectAssign(
|
|
@@ -315,7 +315,7 @@ export function pushManagerFactory(
|
|
|
315
315
|
if (disabled || disconnected === false) return;
|
|
316
316
|
disconnected = false;
|
|
317
317
|
|
|
318
|
-
if (userKey) this.add(userKey, pollingManager.segmentsSyncTask); // client-side
|
|
318
|
+
if (userKey) this.add(userKey, pollingManager.segmentsSyncTask as IMySegmentsSyncTask); // client-side
|
|
319
319
|
else setTimeout(connectPush); // server-side runs in next cycle as in client-side, for consistency with client-side
|
|
320
320
|
},
|
|
321
321
|
|
|
@@ -325,12 +325,12 @@ export function pushManagerFactory(
|
|
|
325
325
|
},
|
|
326
326
|
|
|
327
327
|
// [Only for client-side]
|
|
328
|
-
add(userKey: string, mySegmentsSyncTask:
|
|
328
|
+
add(userKey: string, mySegmentsSyncTask: IMySegmentsSyncTask) {
|
|
329
329
|
const hash = hashUserKey(userKey);
|
|
330
330
|
|
|
331
331
|
if (!userKeyHashes[hash]) {
|
|
332
332
|
userKeyHashes[hash] = userKey;
|
|
333
|
-
clients[userKey] = { hash64: hash64(userKey), worker:
|
|
333
|
+
clients[userKey] = { hash64: hash64(userKey), worker: MySegmentsUpdateWorker(mySegmentsSyncTask) };
|
|
334
334
|
connectForNewClient = true; // we must reconnect on start, to listen the channel for the new user key
|
|
335
335
|
|
|
336
336
|
// Reconnects in case of a new client.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IMySegmentsUpdateData, IMySegmentsUpdateV2Data, ISegmentUpdateData, ISplitUpdateData, ISplitKillData } from './SSEHandler/types';
|
|
2
2
|
import { ITask } from '../types';
|
|
3
|
-
import {
|
|
3
|
+
import { IMySegmentsSyncTask } from '../polling/types';
|
|
4
4
|
import { IEventEmitter } from '../../types';
|
|
5
5
|
import { ControlType } from './constants';
|
|
6
6
|
|
|
@@ -45,6 +45,6 @@ export interface IPushEventEmitter extends IEventEmitter {
|
|
|
45
45
|
*/
|
|
46
46
|
export interface IPushManager extends ITask, IPushEventEmitter {
|
|
47
47
|
// Methods used in client-side, to support multiple clients
|
|
48
|
-
add(userKey: string, mySegmentsSyncTask:
|
|
48
|
+
add(userKey: string, mySegmentsSyncTask: IMySegmentsSyncTask): void,
|
|
49
49
|
remove(userKey: string): void
|
|
50
50
|
}
|
package/src/sync/syncTask.ts
CHANGED
|
@@ -3,9 +3,8 @@ import { ILogger } from '../logger/types';
|
|
|
3
3
|
import { ISyncTask } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Creates
|
|
7
|
-
* The task can be also executed by calling the "execute" method. Multiple
|
|
8
|
-
* For example, submitters executed on SDK destroy or full queue, while periodic execution is pending.
|
|
6
|
+
* Creates an object that handles the periodic execution of a given task via "start" and "stop" methods.
|
|
7
|
+
* The task can be also executed by calling the "execute" method. Multiple calls run sequentially to avoid race conditions (e.g., submitters executed on SDK destroy or full queue, while periodic execution is pending).
|
|
9
8
|
*
|
|
10
9
|
* @param log Logger instance.
|
|
11
10
|
* @param task Task to execute that returns a promise that NEVER REJECTS. Otherwise, periodic execution can result in Unhandled Promise Rejections.
|
|
@@ -15,8 +14,10 @@ import { ISyncTask } from './types';
|
|
|
15
14
|
*/
|
|
16
15
|
export function syncTaskFactory<Input extends any[], Output = any>(log: ILogger, task: (...args: Input) => Promise<Output>, period: number, taskName = 'task'): ISyncTask<Input, Output> {
|
|
17
16
|
|
|
18
|
-
//
|
|
19
|
-
let
|
|
17
|
+
// Flag that indicates if the task is executing
|
|
18
|
+
let executing = 0;
|
|
19
|
+
// Promise chain to resolve tasks sequentially
|
|
20
|
+
let promiseChain: Promise<Output> | undefined;
|
|
20
21
|
// flag that indicates if the task periodic execution has been started/stopped.
|
|
21
22
|
let running = false;
|
|
22
23
|
// Auxiliar counter used to avoid race condition when calling `start` & `stop` intermittently
|
|
@@ -27,20 +28,17 @@ export function syncTaskFactory<Input extends any[], Output = any>(log: ILogger,
|
|
|
27
28
|
let timeoutID: any;
|
|
28
29
|
|
|
29
30
|
function execute(...args: Input): Promise<Output> {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
executing++;
|
|
32
|
+
log.debug(SYNC_TASK_EXECUTE, [taskName]);
|
|
33
|
+
|
|
34
|
+
// Update `promiseChain` with last promise, to run tasks serially
|
|
35
|
+
promiseChain = (promiseChain ? promiseChain.then(() => task(...args)) : task(...args))
|
|
36
|
+
.then(result => {
|
|
37
|
+
executing--;
|
|
38
|
+
return result;
|
|
34
39
|
});
|
|
35
|
-
}
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
log.debug(SYNC_TASK_EXECUTE, [taskName]);
|
|
39
|
-
pendingTask = task(...args).then(result => {
|
|
40
|
-
pendingTask = undefined;
|
|
41
|
-
return result;
|
|
42
|
-
});
|
|
43
|
-
return pendingTask;
|
|
41
|
+
return promiseChain;
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
function periodicExecute(currentRunningId: number) {
|
|
@@ -56,7 +54,7 @@ export function syncTaskFactory<Input extends any[], Output = any>(log: ILogger,
|
|
|
56
54
|
execute,
|
|
57
55
|
|
|
58
56
|
isExecuting() {
|
|
59
|
-
return
|
|
57
|
+
return executing > 0;
|
|
60
58
|
},
|
|
61
59
|
|
|
62
60
|
start(...args: Input) {
|
package/src/utils/Backoff.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export class Backoff {
|
|
2
2
|
|
|
3
|
+
// For testing purposes, assign to overwrite the provided value by param
|
|
4
|
+
static __TEST__BASE_MILLIS?: number;
|
|
5
|
+
static __TEST__MAX_MILLIS?: number;
|
|
6
|
+
|
|
3
7
|
static DEFAULT_BASE_MILLIS = 1000; // 1 second
|
|
4
8
|
static DEFAULT_MAX_MILLIS = 1800000; // 30 minutes
|
|
5
9
|
|
|
@@ -17,8 +21,8 @@ export class Backoff {
|
|
|
17
21
|
* @param {number} maxMillis
|
|
18
22
|
*/
|
|
19
23
|
constructor(cb: (...args: any[]) => any, baseMillis?: number, maxMillis?: number) {
|
|
20
|
-
this.baseMillis = baseMillis || Backoff.DEFAULT_BASE_MILLIS;
|
|
21
|
-
this.maxMillis = maxMillis || Backoff.DEFAULT_MAX_MILLIS;
|
|
24
|
+
this.baseMillis = Backoff.__TEST__BASE_MILLIS || baseMillis || Backoff.DEFAULT_BASE_MILLIS;
|
|
25
|
+
this.maxMillis = Backoff.__TEST__MAX_MILLIS || maxMillis || Backoff.DEFAULT_MAX_MILLIS;
|
|
22
26
|
this.attempts = 0;
|
|
23
27
|
this.cb = cb;
|
|
24
28
|
}
|
|
@@ -32,6 +36,7 @@ export class Backoff {
|
|
|
32
36
|
|
|
33
37
|
if (this.timeoutID) clearTimeout(this.timeoutID);
|
|
34
38
|
this.timeoutID = setTimeout(() => {
|
|
39
|
+
this.timeoutID = undefined;
|
|
35
40
|
this.cb();
|
|
36
41
|
}, delayInMillis);
|
|
37
42
|
this.attempts++;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { IIntegrationFactoryParams } from '../types';
|
|
2
|
+
import SplitToGa from './SplitToGa';
|
|
3
|
+
import { SplitToGoogleAnalyticsOptions } from './types';
|
|
4
|
+
export declare function SplitToGoogleAnalytics(options?: SplitToGoogleAnalyticsOptions): (params: IIntegrationFactoryParams) => SplitToGa;
|
|
@@ -4,7 +4,7 @@ import { ISplitApi } from '../services/types';
|
|
|
4
4
|
import { ISettings } from '../types';
|
|
5
5
|
import { ISyncManager } from '../sync/types';
|
|
6
6
|
/**
|
|
7
|
-
* We'll listen for
|
|
7
|
+
* We'll listen for events over the window object.
|
|
8
8
|
*/
|
|
9
9
|
export declare class BrowserSignalListener implements ISignalListener {
|
|
10
10
|
private syncManager;
|
|
@@ -15,22 +15,22 @@ export declare class BrowserSignalListener implements ISignalListener {
|
|
|
15
15
|
constructor(syncManager: ISyncManager | undefined, settings: ISettings, storage: IStorageSync, serviceApi: ISplitApi);
|
|
16
16
|
/**
|
|
17
17
|
* start method.
|
|
18
|
-
* Called when SplitFactory is initialized.
|
|
19
|
-
* We add a handler on unload events. The handler flushes remaining impressions and events to the backend.
|
|
18
|
+
* Called when SplitFactory is initialized, it adds event listeners to close streaming and flush impressions and events.
|
|
20
19
|
*/
|
|
21
20
|
start(): void;
|
|
22
21
|
/**
|
|
23
22
|
* stop method.
|
|
24
|
-
* Called when client is destroyed.
|
|
25
|
-
* We need to remove the handler for unload events, since it can break if called when Split context was destroyed.
|
|
23
|
+
* Called when client is destroyed, it removes event listeners.
|
|
26
24
|
*/
|
|
27
25
|
stop(): void;
|
|
26
|
+
stopSync(): void;
|
|
28
27
|
/**
|
|
29
28
|
* flushData method.
|
|
30
|
-
* Called when
|
|
29
|
+
* Called when pagehide event is triggered. It flushed remaining impressions and events to the backend,
|
|
31
30
|
* using beacon API if possible, or falling back to regular post transport.
|
|
32
31
|
*/
|
|
33
32
|
flushData(): void;
|
|
33
|
+
flushDataIfHidden(): void;
|
|
34
34
|
private _flushData;
|
|
35
35
|
/**
|
|
36
36
|
* _sendBeacon method.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|