UNPKG

@splitsoftware/splitio-commons

Version:
80 lines (79 loc) 4.02 kB
import { timeout } from '../../../utils/promise/timeout'; import { SDK_SEGMENTS_ARRIVED, SEGMENTS_UPDATE } from '../../../readiness/constants'; import { SYNC_FETCH_RETRY } from '../../../logger/constants'; import { MEMBERSHIPS_LS_UPDATE } from '../../streaming/constants'; import { usesSegmentsSync } from '../../../storages/AbstractDefinitionsCacheSync'; /** * 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, mySegmentsFetcher, storage, segmentsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, matchingKey) { var segments = storage.segments, largeSegments = storage.largeSegments; var readyOnAlreadyExistentState = true; var startingUp = true; /** timeout and telemetry decorator for `splitChangesFetcher` promise */ function _promiseDecorator(promise) { if (startingUp) promise = timeout(requestTimeoutBeforeReady, promise); return promise; } // @TODO if allowing pluggable storages, handle async execution function updateSegments(segmentsData) { var shouldNotifyUpdate; if (segmentsData.type !== undefined) { shouldNotifyUpdate = segmentsData.type === MEMBERSHIPS_LS_UPDATE ? largeSegments.resetSegments(segmentsData) : segments.resetSegments(segmentsData); } else { shouldNotifyUpdate = segments.resetSegments(segmentsData.ms || {}); shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate; } if (storage.save) storage.save(); // Notify update if required if (usesSegmentsSync(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) { readyOnAlreadyExistentState = false; segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED, { type: SEGMENTS_UPDATE, names: [] }); } } function _mySegmentsUpdater(retry, segmentsData, noCache, till) { var updaterPromise = segmentsData ? // If segmentsData is provided, there is no need to fetch mySegments new Promise(function (res) { updateSegments(segmentsData); res(true); }) : // If not provided, fetch mySegments mySegmentsFetcher(matchingKey, noCache, till, _promiseDecorator).then(function (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(function (error) { if (startingUp && retriesOnFailureBeforeReady > retry) { retry += 1; log.warn(SYNC_FETCH_RETRY, ['memberships', 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, noCache, till) { return _mySegmentsUpdater(0, segmentsData, noCache, till); }; }