-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathmySegmentsUpdater.ts
More file actions
104 lines (90 loc) · 4.38 KB
/
Copy pathmySegmentsUpdater.ts
File metadata and controls
104 lines (90 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { IMySegmentsFetcher } from '../fetchers/types';
import { IStorageSync } from '../../../storages/types';
import { ISegmentsEventEmitter } from '../../../readiness/types';
import { timeout } from '../../../utils/promise/timeout';
import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
import { ILogger } from '../../../logger/types';
import { SYNC_MYSEGMENTS_FETCH_RETRY } from '../../../logger/constants';
import { MySegmentsData } from '../types';
import { IMembershipsResponse } from '../../../dtos/types';
import { MEMBERSHIPS_LS_UPDATE } from '../../streaming/constants';
import { usesSegmentsSync } from '../../../storages/AbstractSplitsCacheSync';
type IMySegmentsUpdater = (segmentsData?: MySegmentsData, noCache?: boolean, till?: number) => Promise<boolean>
/**
* factory of MySegments updater, a task that:
* - fetches mySegments using `mySegmentsFetcher`
* - updates `mySegmentsCache`
* - uses `segmentsEventEmitter` to emit events related to segments data updates
*/
export function mySegmentsUpdaterFactory(
log: ILogger,
mySegmentsFetcher: IMySegmentsFetcher,
storage: IStorageSync,
segmentsEventEmitter: ISegmentsEventEmitter,
requestTimeoutBeforeReady: number,
retriesOnFailureBeforeReady: number,
matchingKey: string
): IMySegmentsUpdater {
const { segments, largeSegments } = storage;
let readyOnAlreadyExistentState = true;
let startingUp = true;
/** timeout and telemetry decorator for `splitChangesFetcher` promise */
function _promiseDecorator<T>(promise: Promise<T>) {
if (startingUp) promise = timeout(requestTimeoutBeforeReady, promise);
return promise;
}
// @TODO if allowing pluggable storages, handle async execution
function updateSegments(segmentsData: IMembershipsResponse | MySegmentsData) {
let shouldNotifyUpdate;
if ((segmentsData as MySegmentsData).type !== undefined) {
shouldNotifyUpdate = (segmentsData as MySegmentsData).type === MEMBERSHIPS_LS_UPDATE ?
largeSegments!.resetSegments(segmentsData as MySegmentsData) :
segments.resetSegments(segmentsData as MySegmentsData);
} else {
shouldNotifyUpdate = segments.resetSegments((segmentsData as IMembershipsResponse).ms || {});
shouldNotifyUpdate = largeSegments!.resetSegments((segmentsData as IMembershipsResponse).ls || {}) || shouldNotifyUpdate;
}
if (storage.save) storage.save();
// Notify update if required
if (usesSegmentsSync(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
readyOnAlreadyExistentState = false;
segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED);
}
}
function _mySegmentsUpdater(retry: number, segmentsData?: MySegmentsData, noCache?: boolean, till?: number): Promise<boolean> {
const updaterPromise: Promise<boolean> = segmentsData ?
// If segmentsData is provided, there is no need to fetch mySegments
new Promise((res) => { updateSegments(segmentsData); res(true); }) :
// If not provided, fetch mySegments
mySegmentsFetcher(matchingKey, noCache, till, _promiseDecorator).then(segments => {
updateSegments(segments);
// Only when we have downloaded and stored segments completely, we should not keep retrying anymore
startingUp = false;
return true;
});
return updaterPromise.catch(error => {
if (startingUp && retriesOnFailureBeforeReady > retry) {
retry += 1;
log.warn(SYNC_MYSEGMENTS_FETCH_RETRY, [retry, error]);
return _mySegmentsUpdater(retry); // no need to forward `segmentList` and `noCache` params
} else {
startingUp = false;
}
return false;
});
}
/**
* MySegments updater returns a promise that resolves with a `false` boolean value if it fails to fetch mySegments or synchronize them with the storage.
* Returned promise will not be rejected.
*
* @param segmentsData - it can be:
* (1) the list of mySegments names to sync in the storage,
* (2) an object with a segment name and action (true: add, or false: delete) to update the storage,
* (3) or `undefined`, for which the updater will fetch mySegments in order to sync the storage.
* @param noCache - true to revalidate data to fetch
* @param till - query param to bypass CDN requests
*/
return function mySegmentsUpdater(segmentsData?: MySegmentsData, noCache?: boolean, till?: number) {
return _mySegmentsUpdater(0, segmentsData, noCache, till);
};
}