UNPKG

@splitsoftware/splitio-commons

Version:
93 lines (92 loc) 5.82 kB
import { SDK_SEGMENTS_ARRIVED, SEGMENTS_UPDATE } from '../../../readiness/constants'; import { LOG_PREFIX_INSTANTIATION, LOG_PREFIX_SYNC } from '../../../logger/constants'; import { timeout } from '../../../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. */ export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, readiness, requestTimeoutBeforeReady, retriesOnFailureBeforeReady) { var readyOnAlreadyExistentState = true; function _promiseDecorator(promise) { if (readyOnAlreadyExistentState && requestTimeoutBeforeReady) promise = timeout(requestTimeoutBeforeReady, promise); return promise; } function updateSegment(segmentName, noCache, till, fetchOnlyNew, retries) { log.debug("".concat(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(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(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(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: SEGMENTS_UPDATE, names: [] }; readiness.segments.emit(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(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(LOG_PREFIX_SYNC, "Error while doing fetch of segments. ").concat(error)); } return false; }); }; }