@splitsoftware/splitio-commons
Version:
Split JavaScript SDK common components
97 lines (96 loc) • 6.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.segmentChangesUpdaterFactory = void 0;
var constants_1 = require("../../../readiness/constants");
var constants_2 = require("../../../logger/constants");
var timeout_1 = require("../../../utils/promise/timeout");
/**
* Factory of SegmentChanges updater, a task that:
* - fetches segment changes using `segmentChangesFetcher`
* - updates `segmentsCache`
* - uses `segmentsEventEmitter` to emit events related to segments data updates
*
* @param log - logger instance
* @param segmentChangesFetcher - fetcher of `/segmentChanges`
* @param segments - segments storage, with sync or async methods
* @param readiness - optional readiness manager. Not required for synchronizer or producer mode.
*/
function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, readiness, requestTimeoutBeforeReady, retriesOnFailureBeforeReady) {
var readyOnAlreadyExistentState = true;
function _promiseDecorator(promise) {
if (readyOnAlreadyExistentState && requestTimeoutBeforeReady)
promise = (0, timeout_1.timeout)(requestTimeoutBeforeReady, promise);
return promise;
}
function updateSegment(segmentName, noCache, till, fetchOnlyNew, retries) {
log.debug("".concat(constants_2.LOG_PREFIX_SYNC, "Processing segment ").concat(segmentName));
var sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
return sincePromise.then(function (since) {
// if fetchOnlyNew flag, avoid processing already fetched segments
return fetchOnlyNew && since !== undefined ?
false :
segmentChangesFetcher(since || -1, segmentName, noCache, till, _promiseDecorator).then(function (changes) {
return Promise.all(changes.map(function (x) {
log.debug("".concat(constants_2.LOG_PREFIX_SYNC, "Processing ").concat(segmentName, " with till = ").concat(x.till, ". Added: ").concat(x.added.length, ". Removed: ").concat(x.removed.length));
return segments.update(segmentName, x.added, x.removed, x.till);
})).then(function (updates) {
return updates.some(function (update) { return update; });
});
}).catch(function (error) {
if (retries) {
log.warn("".concat(constants_2.LOG_PREFIX_SYNC, "Retrying fetch of segment ").concat(segmentName, " (attempt #").concat(retries, "). Reason: ").concat(error));
return updateSegment(segmentName, noCache, till, fetchOnlyNew, retries - 1);
}
throw error;
});
});
}
/**
* Segments updater returns a promise that resolves with a `false` boolean value if it fails at least to fetch a segment or synchronize it with the storage.
* Thus, a false result doesn't imply that SDK_SEGMENTS_ARRIVED was not emitted.
* Returned promise will not be rejected.
*
* @param fetchOnlyNew - if true, only fetch the segments that not exists, i.e., which `changeNumber` is equal to -1.
* This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE or RB_SEGMENT_UPDATE notifications.
* @param segmentName - segment name to fetch. By passing `undefined` it fetches the list of segments registered at the storage
* @param noCache - true to revalidate data to fetch on a SEGMENT_UPDATE notifications.
* @param till - till target for the provided segmentName, for CDN bypass.
*/
return function segmentChangesUpdater(fetchOnlyNew, segmentName, noCache, till) {
log.debug("".concat(constants_2.LOG_PREFIX_SYNC, "Started segments update"));
// If not a segment name provided, read list of available segments names to be updated.
var segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments());
return segmentsPromise.then(function (segmentNames) {
var updaters = segmentNames.map(function (segmentName) { return updateSegment(segmentName, noCache, till, fetchOnlyNew, readyOnAlreadyExistentState ? retriesOnFailureBeforeReady : 0); });
return Promise.all(updaters).then(function (shouldUpdateFlags) {
// if at least one segment fetch succeeded, mark segments ready
if (shouldUpdateFlags.some(function (update) { return update; }) || readyOnAlreadyExistentState) {
readyOnAlreadyExistentState = false;
if (readiness && !fetchOnlyNew) {
var metadata = {
type: constants_1.SEGMENTS_UPDATE,
names: []
};
readiness.segments.emit(constants_1.SDK_SEGMENTS_ARRIVED, metadata);
}
}
return true;
});
})
// Handles rejected promises at `segmentChangesFetcher`, `segments.getRegisteredSegments` and other segment storage operations.
.catch(function (error) {
if (error && error.statusCode === 403) {
// If the operation is forbidden, it may be due to permissions. Destroy the SDK instance.
// @TODO although factory status is destroyed, synchronization is not stopped
if (readiness)
readiness.setDestroyed();
log.error("".concat(constants_2.LOG_PREFIX_INSTANTIATION, ": you passed a client-side type authorizationKey, please grab an SDK Key from Harness FME UI that is of type server-side."));
}
else {
log.warn("".concat(constants_2.LOG_PREFIX_SYNC, "Error while doing fetch of segments. ").concat(error));
}
return false;
});
};
}
exports.segmentChangesUpdaterFactory = segmentChangesUpdaterFactory;