@splitsoftware/splitio-commons
Version:
Split JavaScript SDK common components
133 lines (132 loc) • 5.92 kB
JavaScript
import { STREAMING_PARSING_SPLIT_UPDATE } from '../../../logger/constants';
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
import { Backoff } from '../../../utils/Backoff';
import { SPLITS } from '../../../utils/constants';
import { RB_SEGMENT_UPDATE } from '../constants';
import { parseFFUpdatePayload } from '../parseUtils';
import { FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT, FETCH_BACKOFF_MAX_RETRIES } from './constants';
/**
* SplitsUpdateWorker factory
*/
export function SplitsUpdateWorker(log, storage, splitsSyncTask, splitsEventEmitter, telemetryTracker, segmentsSyncTask) {
var ff = SplitsUpdateWorker(storage.splits);
var rbs = SplitsUpdateWorker(storage.rbSegments);
function SplitsUpdateWorker(cache) {
var maxChangeNumber = -1;
var handleNewEvent = false;
var isHandlingEvent;
var cdnBypass;
var instantUpdate;
var backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
function __handleSplitUpdateCall() {
isHandlingEvent = true;
if (maxChangeNumber > cache.getChangeNumber()) {
handleNewEvent = false;
// fetch splits revalidating data if cached
splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined, instantUpdate).then(function () {
if (!isHandlingEvent)
return; // halt if `stop` has been called
if (handleNewEvent) {
__handleSplitUpdateCall();
}
else {
if (instantUpdate)
telemetryTracker.trackUpdatesFromSSE(SPLITS);
// fetch new registered segments for server-side API. Not retrying on error
if (segmentsSyncTask)
segmentsSyncTask.execute(true);
var attempts = backoff.attempts + 1;
if (ff.isSync() && rbs.isSync()) {
log.debug("Refresh completed" + (cdnBypass ? ' bypassing the CDN' : '') + " in " + attempts + " attempts.");
isHandlingEvent = false;
return;
}
if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
backoff.scheduleCall();
return;
}
if (cdnBypass) {
log.debug("No changes fetched after " + attempts + " attempts with CDN bypassed.");
isHandlingEvent = false;
}
else {
backoff.reset();
cdnBypass = true;
__handleSplitUpdateCall();
}
}
});
}
else {
isHandlingEvent = false;
}
}
return {
/**
* Invoked by NotificationProcessor on SPLIT_UPDATE or RB_SEGMENT_UPDATE event
*
* @param changeNumber - change number of the notification
*/
put: function (_a, payload) {
var changeNumber = _a.changeNumber, pcn = _a.pcn, type = _a.type;
var currentChangeNumber = cache.getChangeNumber();
if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber)
return;
maxChangeNumber = changeNumber;
handleNewEvent = true;
cdnBypass = false;
instantUpdate = undefined;
if (payload && currentChangeNumber === pcn) {
instantUpdate = { payload: payload, changeNumber: changeNumber, type: type };
}
if (backoff.timeoutID || !isHandlingEvent)
__handleSplitUpdateCall();
backoff.reset();
},
stop: function () {
isHandlingEvent = false;
backoff.reset();
},
isSync: function () {
return maxChangeNumber <= cache.getChangeNumber();
}
};
}
return {
put: function (parsedData) {
if (parsedData.d && parsedData.c !== undefined) {
try {
var payload = parseFFUpdatePayload(parsedData.c, parsedData.d);
if (payload) {
(parsedData.type === RB_SEGMENT_UPDATE ? rbs : ff).put(parsedData, payload);
return;
}
}
catch (e) {
log.warn(STREAMING_PARSING_SPLIT_UPDATE, [parsedData.type, e]);
}
}
(parsedData.type === RB_SEGMENT_UPDATE ? rbs : ff).put(parsedData);
},
/**
* Invoked by NotificationProcessor on SPLIT_KILL event
*
* @param changeNumber - change number of the notification
* @param splitName - name of split to kill
* @param defaultTreatment - default treatment value
*/
killSplit: function (_a) {
var changeNumber = _a.changeNumber, splitName = _a.splitName, defaultTreatment = _a.defaultTreatment;
if (storage.splits.killLocally(splitName, defaultTreatment, changeNumber)) {
// trigger an SDK_UPDATE if Split was killed locally
splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
}
// queues the SplitChanges fetch (only if changeNumber is newer)
ff.put({ changeNumber: changeNumber });
},
stop: function () {
ff.stop();
rbs.stop();
}
};
}