@splitsoftware/splitio-commons 1.5.1-rc.0 → 1.5.1-rc.1
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 +1 -2
- package/cjs/integrations/ga/GaToSplit.js +11 -12
- 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/integrations/ga/GaToSplit.js +11 -12
- 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 -1
- package/src/integrations/ga/GaToSplit.ts +8 -14
- package/src/integrations/ga/types.ts +2 -13
- 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/types.d.ts +2 -13
- package/types/services/types.d.ts +2 -2
- package/types/sync/polling/fetchers/types.d.ts +2 -2
- package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +2 -2
- 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/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/types.d.ts +1 -2
- package/types/sync/streaming/types.d.ts +2 -2
- package/types/sync/syncTask.d.ts +2 -3
- package/types/utils/Backoff.d.ts +2 -0
- package/src/integrations/ga/autoRequire.js +0 -33
|
@@ -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++;
|
|
@@ -52,23 +52,12 @@ export interface GoogleAnalyticsToSplitOptions {
|
|
|
52
52
|
* If not provided, events are sent using the key and traffic type provided at SDK config
|
|
53
53
|
*/
|
|
54
54
|
identities?: Identity[];
|
|
55
|
-
/**
|
|
56
|
-
* Optional flag to log an error if the `auto-require` script is not detected.
|
|
57
|
-
* The auto-require script automatically requires the `splitTracker` plugin for created trackers,
|
|
58
|
-
* and should be placed right after your Google Analytics, Google Tag Manager or gtag.js script tag.
|
|
59
|
-
*
|
|
60
|
-
* @see {@link https://help.split.io/hc/en-us/articles/360040838752#set-up-with-gtm-and-gtag.js}
|
|
61
|
-
*
|
|
62
|
-
* @property {boolean} autoRequire
|
|
63
|
-
* @default false
|
|
64
|
-
*/
|
|
65
|
-
autoRequire?: boolean;
|
|
66
55
|
}
|
|
67
56
|
/**
|
|
68
57
|
* Enable 'Google Analytics to Split' integration, to track Google Analytics hits as Split events.
|
|
69
58
|
* Used by the browser variant of the isomorphic JS SDK.
|
|
70
59
|
*
|
|
71
|
-
* @see {@link https://help.split.io/hc/en-us/articles/
|
|
60
|
+
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#google-analytics-to-split}
|
|
72
61
|
*/
|
|
73
62
|
export interface IGoogleAnalyticsToSplitConfig extends GoogleAnalyticsToSplitOptions {
|
|
74
63
|
type: 'GOOGLE_ANALYTICS_TO_SPLIT';
|
|
@@ -136,7 +125,7 @@ export interface SplitToGoogleAnalyticsOptions {
|
|
|
136
125
|
* Enable 'Split to Google Analytics' integration, to track Split impressions and events as Google Analytics hits.
|
|
137
126
|
* Used by the browser variant of the isomorphic JS SDK.
|
|
138
127
|
*
|
|
139
|
-
* @see {@link https://help.split.io/hc/en-us/articles/
|
|
128
|
+
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#split-to-google-analytics}
|
|
140
129
|
*/
|
|
141
130
|
export interface ISplitToGoogleAnalyticsConfig extends SplitToGoogleAnalyticsOptions {
|
|
142
131
|
type: 'SPLIT_TO_GOOGLE_ANALYTICS';
|
|
@@ -16,8 +16,8 @@ export declare type IFetch = (url: string, options?: IRequestOptions) => Promise
|
|
|
16
16
|
export declare type IHealthCheckAPI = () => Promise<boolean>;
|
|
17
17
|
export declare type ISplitHttpClient = (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean) => Promise<IResponse>;
|
|
18
18
|
export declare type IFetchAuth = (userKeys?: string[]) => Promise<IResponse>;
|
|
19
|
-
export declare type IFetchSplitChanges = (since: number, noCache?: boolean) => Promise<IResponse>;
|
|
20
|
-
export declare type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean) => Promise<IResponse>;
|
|
19
|
+
export declare type IFetchSplitChanges = (since: number, noCache?: boolean, till?: number) => Promise<IResponse>;
|
|
20
|
+
export declare type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean, till?: number) => Promise<IResponse>;
|
|
21
21
|
export declare type IFetchMySegments = (userMatchingKey: string, noCache?: boolean) => Promise<IResponse>;
|
|
22
22
|
export declare type IPostEventsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
|
|
23
23
|
export declare type IPostTestImpressionsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ISplitChangesResponse, ISegmentChangesResponse } from '../../../dtos/types';
|
|
2
2
|
import { IResponse } from '../../../services/types';
|
|
3
|
-
export declare type ISplitChangesFetcher = (since: number, noCache?: boolean, decorator?: (promise: Promise<IResponse>) => Promise<IResponse>) => Promise<ISplitChangesResponse>;
|
|
4
|
-
export declare type ISegmentChangesFetcher = (since: number, segmentName: string, noCache?: boolean, decorator?: (promise: Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>;
|
|
3
|
+
export declare type ISplitChangesFetcher = (since: number, noCache?: boolean, till?: number, decorator?: (promise: Promise<IResponse>) => Promise<IResponse>) => Promise<ISplitChangesResponse>;
|
|
4
|
+
export declare type ISegmentChangesFetcher = (since: number, segmentName: string, noCache?: boolean, till?: number, decorator?: (promise: Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>;
|
|
5
5
|
export declare type IMySegmentsFetcher = (userMatchingKey: string, noCache?: boolean, decorator?: (promise: Promise<IResponse>) => Promise<IResponse>) => Promise<string[]>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { IStorageSync } from '../../../storages/types';
|
|
2
2
|
import { IReadinessManager } from '../../../readiness/types';
|
|
3
|
-
import {
|
|
3
|
+
import { IMySegmentsSyncTask } from '../types';
|
|
4
4
|
import { IFetchMySegments } from '../../../services/types';
|
|
5
5
|
import { ISettings } from '../../../types';
|
|
6
6
|
/**
|
|
7
7
|
* Creates a sync task that periodically executes a `mySegmentsUpdater` task
|
|
8
8
|
*/
|
|
9
|
-
export declare function mySegmentsSyncTaskFactory(fetchMySegments: IFetchMySegments, storage: IStorageSync, readiness: IReadinessManager, settings: ISettings, matchingKey: string):
|
|
9
|
+
export declare function mySegmentsSyncTaskFactory(fetchMySegments: IFetchMySegments, storage: IStorageSync, readiness: IReadinessManager, settings: ISettings, matchingKey: string): IMySegmentsSyncTask;
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
import { IReadinessManager } from '../../readiness/types';
|
|
2
2
|
import { IStorageSync } from '../../storages/types';
|
|
3
|
-
import { SegmentsData } from '../streaming/SSEHandler/types';
|
|
4
3
|
import { ITask, ISyncTask } from '../types';
|
|
5
|
-
export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean], boolean> {
|
|
4
|
+
export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number], boolean> {
|
|
6
5
|
}
|
|
7
|
-
export interface ISegmentsSyncTask extends ISyncTask<[
|
|
6
|
+
export interface ISegmentsSyncTask extends ISyncTask<[fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number], boolean> {
|
|
7
|
+
}
|
|
8
|
+
export declare type MySegmentsData = string[] | {
|
|
9
|
+
name: string;
|
|
10
|
+
add: boolean;
|
|
11
|
+
};
|
|
12
|
+
export interface IMySegmentsSyncTask extends ISyncTask<[segmentsData?: MySegmentsData, noCache?: boolean], boolean> {
|
|
8
13
|
}
|
|
9
14
|
export interface IPollingManager extends ITask {
|
|
10
15
|
syncAll(): Promise<any>;
|
|
11
16
|
splitsSyncTask: ISplitsSyncTask;
|
|
12
|
-
segmentsSyncTask:
|
|
17
|
+
segmentsSyncTask: ISyncTask;
|
|
13
18
|
}
|
|
14
19
|
/**
|
|
15
20
|
* PollingManager for client-side with support for multiple clients
|
|
16
21
|
*/
|
|
17
22
|
export interface IPollingManagerCS extends IPollingManager {
|
|
18
|
-
add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync):
|
|
23
|
+
add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync): IMySegmentsSyncTask;
|
|
19
24
|
remove(matchingKey: string): void;
|
|
20
|
-
get(matchingKey: string):
|
|
25
|
+
get(matchingKey: string): IMySegmentsSyncTask | undefined;
|
|
21
26
|
}
|
|
@@ -2,7 +2,7 @@ import { ISegmentChangesFetcher } from '../fetchers/types';
|
|
|
2
2
|
import { ISegmentsCacheBase } from '../../../storages/types';
|
|
3
3
|
import { IReadinessManager } from '../../../readiness/types';
|
|
4
4
|
import { ILogger } from '../../../logger/types';
|
|
5
|
-
declare type ISegmentChangesUpdater = (
|
|
5
|
+
declare type ISegmentChangesUpdater = (fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number) => Promise<boolean>;
|
|
6
6
|
/**
|
|
7
7
|
* Factory of SegmentChanges updater, a task that:
|
|
8
8
|
* - fetches segment changes using `segmentChangesFetcher`
|
|
@@ -4,7 +4,7 @@ import { ISplitChangesFetcher } from '../fetchers/types';
|
|
|
4
4
|
import { ISplit } from '../../../dtos/types';
|
|
5
5
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
6
6
|
import { ILogger } from '../../../logger/types';
|
|
7
|
-
declare type ISplitChangesUpdater = (noCache?: boolean) => Promise<boolean>;
|
|
7
|
+
declare type ISplitChangesUpdater = (noCache?: boolean, till?: number) => Promise<boolean>;
|
|
8
8
|
/**
|
|
9
9
|
* Collect segments from a raw split definition.
|
|
10
10
|
* Exported for testing purposes.
|
|
@@ -1,27 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Backoff } from '../../../utils/Backoff';
|
|
1
|
+
import { IMySegmentsSyncTask } from '../../polling/types';
|
|
3
2
|
import { IUpdateWorker } from './types';
|
|
4
|
-
import { SegmentsData } from '../SSEHandler/types';
|
|
5
3
|
/**
|
|
6
|
-
* MySegmentsUpdateWorker
|
|
4
|
+
* MySegmentsUpdateWorker factory
|
|
7
5
|
*/
|
|
8
|
-
export declare
|
|
9
|
-
private readonly mySegmentsSyncTask;
|
|
10
|
-
private maxChangeNumber;
|
|
11
|
-
private handleNewEvent;
|
|
12
|
-
private segmentsData?;
|
|
13
|
-
private currentChangeNumber;
|
|
14
|
-
readonly backoff: Backoff;
|
|
15
|
-
/**
|
|
16
|
-
* @param {Object} mySegmentsSyncTask task for syncing mySegments data
|
|
17
|
-
*/
|
|
18
|
-
constructor(mySegmentsSyncTask: ISegmentsSyncTask);
|
|
19
|
-
__handleMySegmentsUpdateCall(): void;
|
|
20
|
-
/**
|
|
21
|
-
* Invoked by NotificationProcessor on MY_SEGMENTS_UPDATE event
|
|
22
|
-
*
|
|
23
|
-
* @param {number} changeNumber change number of the MY_SEGMENTS_UPDATE notification
|
|
24
|
-
* @param {SegmentsData | undefined} segmentsData might be undefined
|
|
25
|
-
*/
|
|
26
|
-
put(changeNumber: number, segmentsData?: SegmentsData): void;
|
|
27
|
-
}
|
|
6
|
+
export declare function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask): IUpdateWorker;
|
|
@@ -1,28 +1,8 @@
|
|
|
1
|
+
import { ILogger } from '../../../logger/types';
|
|
1
2
|
import { ISegmentsCacheSync } from '../../../storages/types';
|
|
2
|
-
import { Backoff } from '../../../utils/Backoff';
|
|
3
3
|
import { ISegmentsSyncTask } from '../../polling/types';
|
|
4
|
-
import { ISegmentUpdateData } from '../SSEHandler/types';
|
|
5
4
|
import { IUpdateWorker } from './types';
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
6
|
+
* SegmentsUpdateWorker factory
|
|
8
7
|
*/
|
|
9
|
-
export declare
|
|
10
|
-
private readonly segmentsCache;
|
|
11
|
-
private readonly segmentsSyncTask;
|
|
12
|
-
private readonly maxChangeNumbers;
|
|
13
|
-
private handleNewEvent;
|
|
14
|
-
readonly backoff: Backoff;
|
|
15
|
-
/**
|
|
16
|
-
* @param {Object} segmentsCache segments data cache
|
|
17
|
-
* @param {Object} segmentsSyncTask task for syncing segments data
|
|
18
|
-
*/
|
|
19
|
-
constructor(segmentsSyncTask: ISegmentsSyncTask, segmentsCache: ISegmentsCacheSync);
|
|
20
|
-
__handleSegmentUpdateCall(): void;
|
|
21
|
-
/**
|
|
22
|
-
* Invoked by NotificationProcessor on SEGMENT_UPDATE event
|
|
23
|
-
*
|
|
24
|
-
* @param {number} changeNumber change number of the SEGMENT_UPDATE notification
|
|
25
|
-
* @param {string} segmentName segment name of the SEGMENT_UPDATE notification
|
|
26
|
-
*/
|
|
27
|
-
put({ changeNumber, segmentName }: ISegmentUpdateData): void;
|
|
28
|
-
}
|
|
8
|
+
export declare function SegmentsUpdateWorker(log: ILogger, segmentsSyncTask: ISegmentsSyncTask, segmentsCache: ISegmentsCacheSync): IUpdateWorker;
|
|
@@ -1,39 +1,12 @@
|
|
|
1
|
+
import { ILogger } from '../../../logger/types';
|
|
1
2
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
2
3
|
import { ISplitsCacheSync } from '../../../storages/types';
|
|
3
|
-
import { Backoff } from '../../../utils/Backoff';
|
|
4
4
|
import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
|
|
5
|
-
import { ISplitKillData
|
|
5
|
+
import { ISplitKillData } from '../SSEHandler/types';
|
|
6
6
|
import { IUpdateWorker } from './types';
|
|
7
7
|
/**
|
|
8
|
-
* SplitsUpdateWorker
|
|
8
|
+
* SplitsUpdateWorker factory
|
|
9
9
|
*/
|
|
10
|
-
export declare
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
private readonly splitsEventEmitter;
|
|
14
|
-
private readonly segmentsSyncTask?;
|
|
15
|
-
private maxChangeNumber;
|
|
16
|
-
private handleNewEvent;
|
|
17
|
-
readonly backoff: Backoff;
|
|
18
|
-
/**
|
|
19
|
-
* @param {Object} splitsCache splits data cache
|
|
20
|
-
* @param {Object} splitsSyncTask task for syncing splits data
|
|
21
|
-
* @param {Object} splitsEventEmitter emitter for splits data events
|
|
22
|
-
*/
|
|
23
|
-
constructor(splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, segmentsSyncTask?: ISegmentsSyncTask);
|
|
24
|
-
__handleSplitUpdateCall(): void;
|
|
25
|
-
/**
|
|
26
|
-
* Invoked by NotificationProcessor on SPLIT_UPDATE event
|
|
27
|
-
*
|
|
28
|
-
* @param {number} changeNumber change number of the SPLIT_UPDATE notification
|
|
29
|
-
*/
|
|
30
|
-
put({ changeNumber }: Pick<ISplitUpdateData, 'changeNumber'>): void;
|
|
31
|
-
/**
|
|
32
|
-
* Invoked by NotificationProcessor on SPLIT_KILL event
|
|
33
|
-
*
|
|
34
|
-
* @param {number} changeNumber change number of the SPLIT_UPDATE notification
|
|
35
|
-
* @param {string} splitName name of split to kill
|
|
36
|
-
* @param {string} defaultTreatment default treatment value
|
|
37
|
-
*/
|
|
38
|
-
killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData): void;
|
|
39
|
-
}
|
|
10
|
+
export declare function SplitsUpdateWorker(log: ILogger, splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker & {
|
|
11
|
+
killSplit(event: ISplitKillData): void;
|
|
12
|
+
};
|