import { IMySegmentsSyncTask, IPollingManagerCS } from './types'; import { forOwn } from '../../utils/lang'; import { IReadinessManager } from '../../readiness/types'; import { IStorageSync } from '../../storages/types'; import { mySegmentsSyncTaskFactory } from './syncTasks/mySegmentsSyncTask'; import { splitsSyncTaskFactory } from './syncTasks/splitsSyncTask'; import { getMatching } from '../../utils/key'; import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../../readiness/constants'; import { POLLING_SMART_PAUSING, POLLING_START, POLLING_STOP } from '../../logger/constants'; import { ISdkFactoryContextSync } from '../../sdkFactory/types'; import { usesSegmentsSync } from '../../storages/AbstractSplitsCacheSync'; import { SdkUpdateMetadata } from '../../../types/splitio'; /** * Expose start / stop mechanism for polling data from services. * For client-side API with multiple clients. */ export function pollingManagerCSFactory( params: ISdkFactoryContextSync ): IPollingManagerCS { const { splitApi, storage, readiness, settings } = params; const log = settings.log; const splitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings, true); // Map of matching keys to their corresponding MySegmentsSyncTask. const mySegmentsSyncTasks: Record = {}; const matchingKey = getMatching(settings.core.key); const mySegmentsSyncTask = add(matchingKey, readiness, storage); function startMySegmentsSyncTasks() { forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => { mySegmentsSyncTask.start(); }); } function stopMySegmentsSyncTasks() { forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => { if (mySegmentsSyncTask.isRunning()) mySegmentsSyncTask.stop(); }); } // smart pausing readiness.splits.on(SDK_SPLITS_ARRIVED, () => { if (!splitsSyncTask.isRunning()) return; // noop if not doing polling const usingSegments = usesSegmentsSync(storage); if (usingSegments !== mySegmentsSyncTask.isRunning()) { log.info(POLLING_SMART_PAUSING, [usingSegments ? 'ON' : 'OFF']); if (usingSegments) { startMySegmentsSyncTasks(); } else { stopMySegmentsSyncTasks(); } } }); function add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync) { const mySegmentsSyncTask = mySegmentsSyncTaskFactory(splitApi.fetchMemberships, storage, readiness, settings, matchingKey); // smart ready function smartReady(metadata: SdkUpdateMetadata) { if (!readiness.isReady() && !usesSegmentsSync(storage)) readiness.segments.emit(SDK_SEGMENTS_ARRIVED, metadata); } if (!usesSegmentsSync(storage)) setTimeout(smartReady, 0); else readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady); mySegmentsSyncTasks[matchingKey] = mySegmentsSyncTask; return mySegmentsSyncTask; } return { splitsSyncTask, segmentsSyncTask: mySegmentsSyncTask, // Start periodic fetching (polling) start() { log.info(POLLING_START); splitsSyncTask.start(); if (usesSegmentsSync(storage)) startMySegmentsSyncTasks(); }, // Stop periodic fetching (polling) stop() { log.info(POLLING_STOP); if (splitsSyncTask.isRunning()) splitsSyncTask.stop(); stopMySegmentsSyncTasks(); }, // Used by SyncManager to know if running in polling mode. isRunning: splitsSyncTask.isRunning, // fetch splits and segments syncAll() { const promises = [splitsSyncTask.execute()]; forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => { promises.push(mySegmentsSyncTask.execute()); }); return Promise.all(promises); }, // Support for handling mySegments sync of multiple clients add, remove(matchingKey: string) { delete mySegmentsSyncTasks[matchingKey]; }, get(matchingKey: string) { return mySegmentsSyncTasks[matchingKey]; } }; }