@splitsoftware/splitio-commons 2.2.1-rc.3 → 2.2.1-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +5 -2
- package/README.md +1 -0
- package/cjs/consent/sdkUserConsent.js +5 -3
- package/cjs/evaluator/combiners/and.js +2 -6
- package/cjs/evaluator/combiners/ifelseif.js +6 -6
- package/cjs/evaluator/condition/index.js +6 -5
- package/cjs/evaluator/index.js +7 -7
- package/cjs/evaluator/matchers/index.js +3 -1
- package/cjs/evaluator/matchers/matcherTypes.js +1 -0
- package/cjs/evaluator/matchers/rbsegment.js +56 -0
- package/cjs/evaluator/matchersTransform/index.js +4 -0
- package/cjs/evaluator/parser/index.js +2 -2
- package/cjs/evaluator/value/sanitize.js +1 -0
- package/cjs/listeners/browser.js +2 -5
- package/cjs/logger/constants.js +4 -3
- package/cjs/logger/messages/debug.js +3 -2
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/services/splitApi.js +3 -4
- package/cjs/services/splitHttpClient.js +3 -1
- package/cjs/storages/AbstractSplitsCacheSync.js +5 -2
- package/cjs/storages/KeyBuilder.js +9 -0
- package/cjs/storages/KeyBuilderCS.js +3 -0
- package/cjs/storages/KeyBuilderSS.js +3 -0
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +117 -0
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +9 -14
- package/cjs/storages/inLocalStorage/index.js +5 -1
- package/cjs/storages/inLocalStorage/validateCache.js +2 -1
- package/cjs/storages/inMemory/InMemoryStorage.js +3 -0
- package/cjs/storages/inMemory/InMemoryStorageCS.js +4 -0
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +61 -0
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +1 -0
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +64 -0
- package/cjs/storages/inRedis/index.js +5 -3
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +64 -0
- package/cjs/storages/pluggable/index.js +2 -0
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +54 -4
- package/cjs/sync/polling/pollingManagerCS.js +7 -7
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +59 -33
- package/cjs/sync/streaming/SSEHandler/index.js +1 -0
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +106 -77
- package/cjs/sync/streaming/constants.js +2 -1
- package/cjs/sync/streaming/pushManager.js +3 -16
- package/cjs/sync/syncManagerOnline.js +2 -2
- package/cjs/utils/constants/index.js +6 -2
- package/esm/consent/sdkUserConsent.js +5 -3
- package/esm/evaluator/combiners/and.js +2 -6
- package/esm/evaluator/combiners/ifelseif.js +7 -7
- package/esm/evaluator/condition/index.js +6 -5
- package/esm/evaluator/index.js +7 -7
- package/esm/evaluator/matchers/index.js +3 -1
- package/esm/evaluator/matchers/matcherTypes.js +1 -0
- package/esm/evaluator/matchers/rbsegment.js +52 -0
- package/esm/evaluator/matchersTransform/index.js +4 -0
- package/esm/evaluator/parser/index.js +2 -2
- package/esm/evaluator/value/sanitize.js +1 -0
- package/esm/listeners/browser.js +2 -5
- package/esm/logger/constants.js +1 -0
- package/esm/logger/messages/debug.js +3 -2
- package/esm/logger/messages/warn.js +1 -1
- package/esm/services/splitApi.js +3 -4
- package/esm/services/splitHttpClient.js +3 -1
- package/esm/storages/AbstractSplitsCacheSync.js +5 -2
- package/esm/storages/KeyBuilder.js +9 -0
- package/esm/storages/KeyBuilderCS.js +3 -0
- package/esm/storages/KeyBuilderSS.js +3 -0
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +114 -0
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +9 -14
- package/esm/storages/inLocalStorage/index.js +5 -1
- package/esm/storages/inLocalStorage/validateCache.js +2 -1
- package/esm/storages/inMemory/InMemoryStorage.js +3 -0
- package/esm/storages/inMemory/InMemoryStorageCS.js +4 -0
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +58 -0
- package/esm/storages/inMemory/SplitsCacheInMemory.js +1 -0
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +61 -0
- package/esm/storages/inRedis/index.js +5 -3
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +61 -0
- package/esm/storages/pluggable/index.js +2 -0
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +54 -4
- package/esm/sync/polling/pollingManagerCS.js +7 -7
- package/esm/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/esm/sync/polling/updaters/splitChangesUpdater.js +59 -33
- package/esm/sync/streaming/SSEHandler/index.js +2 -1
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +102 -73
- package/esm/sync/streaming/constants.js +1 -0
- package/esm/sync/streaming/pushManager.js +6 -19
- package/esm/sync/syncManagerOnline.js +2 -2
- package/esm/utils/constants/index.js +5 -1
- package/package.json +1 -1
- package/src/consent/sdkUserConsent.ts +3 -2
- package/src/dtos/types.ts +37 -8
- package/src/evaluator/Engine.ts +1 -1
- package/src/evaluator/combiners/and.ts +5 -4
- package/src/evaluator/combiners/ifelseif.ts +7 -9
- package/src/evaluator/condition/engineUtils.ts +1 -1
- package/src/evaluator/condition/index.ts +12 -12
- package/src/evaluator/index.ts +7 -7
- package/src/evaluator/matchers/index.ts +3 -1
- package/src/evaluator/matchers/matcherTypes.ts +1 -0
- package/src/evaluator/matchers/rbsegment.ts +74 -0
- package/src/evaluator/matchersTransform/index.ts +3 -0
- package/src/evaluator/parser/index.ts +3 -3
- package/src/evaluator/types.ts +2 -2
- package/src/evaluator/value/index.ts +2 -2
- package/src/evaluator/value/sanitize.ts +5 -4
- package/src/listeners/browser.ts +2 -3
- package/src/logger/constants.ts +1 -0
- package/src/logger/messages/debug.ts +3 -2
- package/src/logger/messages/warn.ts +1 -1
- package/src/sdkManager/index.ts +1 -1
- package/src/services/splitApi.ts +3 -4
- package/src/services/splitHttpClient.ts +3 -1
- package/src/services/types.ts +1 -1
- package/src/storages/AbstractSplitsCacheSync.ts +6 -3
- package/src/storages/KeyBuilder.ts +12 -0
- package/src/storages/KeyBuilderCS.ts +4 -0
- package/src/storages/KeyBuilderSS.ts +4 -0
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +136 -0
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +10 -14
- package/src/storages/inLocalStorage/index.ts +5 -1
- package/src/storages/inLocalStorage/validateCache.ts +3 -1
- package/src/storages/inMemory/InMemoryStorage.ts +3 -0
- package/src/storages/inMemory/InMemoryStorageCS.ts +4 -0
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +68 -0
- package/src/storages/inMemory/SplitsCacheInMemory.ts +1 -0
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +79 -0
- package/src/storages/inRedis/index.ts +5 -3
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +76 -0
- package/src/storages/pluggable/index.ts +2 -0
- package/src/storages/types.ts +33 -1
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +65 -4
- package/src/sync/polling/fetchers/types.ts +1 -0
- package/src/sync/polling/pollingManagerCS.ts +7 -7
- package/src/sync/polling/syncTasks/splitsSyncTask.ts +1 -1
- package/src/sync/polling/types.ts +2 -2
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -2
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
- package/src/sync/polling/updaters/splitChangesUpdater.ts +70 -43
- package/src/sync/streaming/SSEHandler/index.ts +2 -1
- package/src/sync/streaming/SSEHandler/types.ts +2 -2
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +98 -68
- package/src/sync/streaming/constants.ts +1 -0
- package/src/sync/streaming/parseUtils.ts +2 -2
- package/src/sync/streaming/pushManager.ts +6 -18
- package/src/sync/streaming/types.ts +3 -2
- package/src/sync/syncManagerOnline.ts +2 -2
- package/src/utils/constants/index.ts +6 -1
- package/src/utils/lang/index.ts +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { isNaNNumber } from '../../utils/lang';
|
|
2
|
+
import { LOG_PREFIX } from './constants';
|
|
3
|
+
import { setToArray } from '../../utils/lang/sets';
|
|
4
|
+
var RBSegmentsCacheInRedis = /** @class */ (function () {
|
|
5
|
+
function RBSegmentsCacheInRedis(log, keys, redis) {
|
|
6
|
+
this.log = log;
|
|
7
|
+
this.keys = keys;
|
|
8
|
+
this.redis = redis;
|
|
9
|
+
}
|
|
10
|
+
RBSegmentsCacheInRedis.prototype.get = function (name) {
|
|
11
|
+
return this.redis.get(this.keys.buildRBSegmentKey(name))
|
|
12
|
+
.then(function (maybeRBSegment) { return maybeRBSegment && JSON.parse(maybeRBSegment); });
|
|
13
|
+
};
|
|
14
|
+
RBSegmentsCacheInRedis.prototype.getNames = function () {
|
|
15
|
+
var _this = this;
|
|
16
|
+
return this.redis.keys(this.keys.searchPatternForRBSegmentKeys()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
|
|
17
|
+
};
|
|
18
|
+
RBSegmentsCacheInRedis.prototype.contains = function (names) {
|
|
19
|
+
var namesArray = setToArray(names);
|
|
20
|
+
return this.getNames().then(function (namesInStorage) {
|
|
21
|
+
return namesArray.every(function (name) { return namesInStorage.includes(name); });
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
RBSegmentsCacheInRedis.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
25
|
+
var _this = this;
|
|
26
|
+
return Promise.all([
|
|
27
|
+
this.setChangeNumber(changeNumber),
|
|
28
|
+
Promise.all(toAdd.map(function (toAdd) {
|
|
29
|
+
var key = _this.keys.buildRBSegmentKey(toAdd.name);
|
|
30
|
+
var stringifiedNewRBSegment = JSON.stringify(toAdd);
|
|
31
|
+
return _this.redis.set(key, stringifiedNewRBSegment).then(function () { return true; });
|
|
32
|
+
})),
|
|
33
|
+
Promise.all(toRemove.map(function (toRemove) {
|
|
34
|
+
var key = _this.keys.buildRBSegmentKey(toRemove.name);
|
|
35
|
+
return _this.redis.del(key).then(function (status) { return status === 1; });
|
|
36
|
+
}))
|
|
37
|
+
]).then(function (_a) {
|
|
38
|
+
var added = _a[1], removed = _a[2];
|
|
39
|
+
return added.some(function (result) { return result; }) || removed.some(function (result) { return result; });
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
RBSegmentsCacheInRedis.prototype.setChangeNumber = function (changeNumber) {
|
|
43
|
+
return this.redis.set(this.keys.buildRBSegmentsTillKey(), changeNumber + '').then(function (status) { return status === 'OK'; });
|
|
44
|
+
};
|
|
45
|
+
RBSegmentsCacheInRedis.prototype.getChangeNumber = function () {
|
|
46
|
+
var _this = this;
|
|
47
|
+
return this.redis.get(this.keys.buildRBSegmentsTillKey()).then(function (value) {
|
|
48
|
+
var i = parseInt(value, 10);
|
|
49
|
+
return isNaNNumber(i) ? -1 : i;
|
|
50
|
+
}).catch(function (e) {
|
|
51
|
+
_this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from storage. Error: ' + e);
|
|
52
|
+
return -1;
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
// @TODO implement if required by DataLoader or producer mode
|
|
56
|
+
RBSegmentsCacheInRedis.prototype.clear = function () {
|
|
57
|
+
return Promise.resolve();
|
|
58
|
+
};
|
|
59
|
+
return RBSegmentsCacheInRedis;
|
|
60
|
+
}());
|
|
61
|
+
export { RBSegmentsCacheInRedis };
|
|
@@ -9,6 +9,7 @@ import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
|
|
|
9
9
|
import { UniqueKeysCacheInRedis } from './UniqueKeysCacheInRedis';
|
|
10
10
|
import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
|
|
11
11
|
import { metadataBuilder } from '../utils';
|
|
12
|
+
import { RBSegmentsCacheInRedis } from './RBSegmentsCacheInRedis';
|
|
12
13
|
var RD;
|
|
13
14
|
try {
|
|
14
15
|
// Using `require` to prevent error when bundling or importing the SDK in a .mjs file, since ioredis is a CommonJS module.
|
|
@@ -25,7 +26,7 @@ export function InRedisStorage(options) {
|
|
|
25
26
|
var prefix = validatePrefix(options.prefix);
|
|
26
27
|
function InRedisStorageFactory(params) {
|
|
27
28
|
if (!RD)
|
|
28
|
-
throw new Error('The SDK Redis storage is
|
|
29
|
+
throw new Error('The SDK Redis storage is unavailable. Make sure your runtime environment supports CommonJS (`require`) so the `ioredis` dependency can be imported.');
|
|
29
30
|
var onReadyFromCacheCb = params.onReadyFromCacheCb, onReadyCb = params.onReadyCb, settings = params.settings, log = params.settings.log;
|
|
30
31
|
var metadata = metadataBuilder(settings);
|
|
31
32
|
var keys = new KeyBuilderSS(prefix, metadata);
|
|
@@ -33,9 +34,9 @@ export function InRedisStorage(options) {
|
|
|
33
34
|
var telemetry = new TelemetryCacheInRedis(log, keys, redisClient);
|
|
34
35
|
var impressionCountsCache = new ImpressionCountsCacheInRedis(log, keys.buildImpressionsCountKey(), redisClient);
|
|
35
36
|
var uniqueKeysCache = new UniqueKeysCacheInRedis(log, keys.buildUniqueKeysKey(), redisClient);
|
|
36
|
-
// RedisAdapter
|
|
37
|
+
// RedisAdapter lets queue operations before connected
|
|
37
38
|
onReadyFromCacheCb();
|
|
38
|
-
//
|
|
39
|
+
// Subscription to Redis connect event in order to emit SDK_READY event on consumer mode
|
|
39
40
|
redisClient.on('connect', function () {
|
|
40
41
|
onReadyCb();
|
|
41
42
|
impressionCountsCache.start();
|
|
@@ -45,6 +46,7 @@ export function InRedisStorage(options) {
|
|
|
45
46
|
});
|
|
46
47
|
return {
|
|
47
48
|
splits: new SplitsCacheInRedis(log, keys, redisClient, settings.sync.__splitFiltersValidation),
|
|
49
|
+
rbSegments: new RBSegmentsCacheInRedis(log, keys, redisClient),
|
|
48
50
|
segments: new SegmentsCacheInRedis(log, keys, redisClient),
|
|
49
51
|
impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
|
|
50
52
|
impressionCounts: impressionCountsCache,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { isNaNNumber } from '../../utils/lang';
|
|
2
|
+
import { LOG_PREFIX } from './constants';
|
|
3
|
+
import { setToArray } from '../../utils/lang/sets';
|
|
4
|
+
var RBSegmentsCachePluggable = /** @class */ (function () {
|
|
5
|
+
function RBSegmentsCachePluggable(log, keys, wrapper) {
|
|
6
|
+
this.log = log;
|
|
7
|
+
this.keys = keys;
|
|
8
|
+
this.wrapper = wrapper;
|
|
9
|
+
}
|
|
10
|
+
RBSegmentsCachePluggable.prototype.get = function (name) {
|
|
11
|
+
return this.wrapper.get(this.keys.buildRBSegmentKey(name))
|
|
12
|
+
.then(function (maybeRBSegment) { return maybeRBSegment && JSON.parse(maybeRBSegment); });
|
|
13
|
+
};
|
|
14
|
+
RBSegmentsCachePluggable.prototype.getNames = function () {
|
|
15
|
+
var _this = this;
|
|
16
|
+
return this.wrapper.getKeysByPrefix(this.keys.buildRBSegmentKeyPrefix()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
|
|
17
|
+
};
|
|
18
|
+
RBSegmentsCachePluggable.prototype.contains = function (names) {
|
|
19
|
+
var namesArray = setToArray(names);
|
|
20
|
+
return this.getNames().then(function (namesInStorage) {
|
|
21
|
+
return namesArray.every(function (name) { return namesInStorage.includes(name); });
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
RBSegmentsCachePluggable.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
25
|
+
var _this = this;
|
|
26
|
+
return Promise.all([
|
|
27
|
+
this.setChangeNumber(changeNumber),
|
|
28
|
+
Promise.all(toAdd.map(function (toAdd) {
|
|
29
|
+
var key = _this.keys.buildRBSegmentKey(toAdd.name);
|
|
30
|
+
var stringifiedNewRBSegment = JSON.stringify(toAdd);
|
|
31
|
+
return _this.wrapper.set(key, stringifiedNewRBSegment).then(function () { return true; });
|
|
32
|
+
})),
|
|
33
|
+
Promise.all(toRemove.map(function (toRemove) {
|
|
34
|
+
var key = _this.keys.buildRBSegmentKey(toRemove.name);
|
|
35
|
+
return _this.wrapper.del(key);
|
|
36
|
+
}))
|
|
37
|
+
]).then(function (_a) {
|
|
38
|
+
var added = _a[1], removed = _a[2];
|
|
39
|
+
return added.some(function (result) { return result; }) || removed.some(function (result) { return result; });
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
RBSegmentsCachePluggable.prototype.setChangeNumber = function (changeNumber) {
|
|
43
|
+
return this.wrapper.set(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
|
|
44
|
+
};
|
|
45
|
+
RBSegmentsCachePluggable.prototype.getChangeNumber = function () {
|
|
46
|
+
var _this = this;
|
|
47
|
+
return this.wrapper.get(this.keys.buildRBSegmentsTillKey()).then(function (value) {
|
|
48
|
+
var i = parseInt(value, 10);
|
|
49
|
+
return isNaNNumber(i) ? -1 : i;
|
|
50
|
+
}).catch(function (e) {
|
|
51
|
+
_this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from storage. Error: ' + e);
|
|
52
|
+
return -1;
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
// @TODO implement if required by DataLoader or producer mode
|
|
56
|
+
RBSegmentsCachePluggable.prototype.clear = function () {
|
|
57
|
+
return Promise.resolve();
|
|
58
|
+
};
|
|
59
|
+
return RBSegmentsCachePluggable;
|
|
60
|
+
}());
|
|
61
|
+
export { RBSegmentsCachePluggable };
|
|
@@ -19,6 +19,7 @@ import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
|
|
|
19
19
|
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
|
|
20
20
|
import { metadataBuilder } from '../utils';
|
|
21
21
|
import { LOG_PREFIX } from '../pluggable/constants';
|
|
22
|
+
import { RBSegmentsCachePluggable } from './RBSegmentsCachePluggable';
|
|
22
23
|
var NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
|
|
23
24
|
var NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
|
|
24
25
|
/**
|
|
@@ -102,6 +103,7 @@ export function PluggableStorage(options) {
|
|
|
102
103
|
});
|
|
103
104
|
return {
|
|
104
105
|
splits: new SplitsCachePluggable(log, keys, wrapper, settings.sync.__splitFiltersValidation),
|
|
106
|
+
rbSegments: new RBSegmentsCachePluggable(log, keys, wrapper),
|
|
105
107
|
segments: new SegmentsCachePluggable(log, keys, wrapper),
|
|
106
108
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
107
109
|
impressionCounts: impressionCountsCache,
|
|
@@ -1,14 +1,64 @@
|
|
|
1
|
+
import { FLAG_SPEC_VERSION } from '../../../utils/constants';
|
|
2
|
+
import { base } from '../../../utils/settingsValidation';
|
|
3
|
+
import { LOG_PREFIX_SYNC_SPLITS } from '../../../logger/constants';
|
|
4
|
+
var PROXY_CHECK_INTERVAL_MILLIS_CS = 60 * 60 * 1000; // 1 hour in Client Side
|
|
5
|
+
var PROXY_CHECK_INTERVAL_MILLIS_SS = 24 * PROXY_CHECK_INTERVAL_MILLIS_CS; // 24 hours in Server Side
|
|
6
|
+
function sdkEndpointOverridden(settings) {
|
|
7
|
+
return settings.urls.sdk !== base.urls.sdk;
|
|
8
|
+
}
|
|
1
9
|
/**
|
|
2
10
|
* Factory of SplitChanges fetcher.
|
|
3
11
|
* SplitChanges fetcher is a wrapper around `splitChanges` API service that parses the response and handle errors.
|
|
4
12
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
13
|
+
// @TODO breaking: drop support for Split Proxy below v5.10.0 and simplify the implementation
|
|
14
|
+
export function splitChangesFetcherFactory(fetchSplitChanges, settings, storage) {
|
|
15
|
+
var log = settings.log;
|
|
16
|
+
var PROXY_CHECK_INTERVAL_MILLIS = settings.core.key !== undefined ? PROXY_CHECK_INTERVAL_MILLIS_CS : PROXY_CHECK_INTERVAL_MILLIS_SS;
|
|
17
|
+
var lastProxyCheckTimestamp;
|
|
18
|
+
return function splitChangesFetcher(since, noCache, till, rbSince,
|
|
7
19
|
// Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
|
|
8
20
|
decorator) {
|
|
9
|
-
|
|
21
|
+
// Recheck proxy
|
|
22
|
+
if (lastProxyCheckTimestamp && (Date.now() - lastProxyCheckTimestamp) > PROXY_CHECK_INTERVAL_MILLIS) {
|
|
23
|
+
settings.sync.flagSpecVersion = FLAG_SPEC_VERSION;
|
|
24
|
+
}
|
|
25
|
+
var splitsPromise = fetchSplitChanges(since, noCache, till, settings.sync.flagSpecVersion === FLAG_SPEC_VERSION ? rbSince : undefined)
|
|
26
|
+
// Handle proxy error with spec 1.3
|
|
27
|
+
.catch(function (err) {
|
|
28
|
+
if (err.statusCode === 400 && sdkEndpointOverridden(settings) && settings.sync.flagSpecVersion === FLAG_SPEC_VERSION) {
|
|
29
|
+
log.error(LOG_PREFIX_SYNC_SPLITS + 'Proxy error detected. Retrying with spec 1.2. If you are using Split Proxy, please upgrade to latest version');
|
|
30
|
+
lastProxyCheckTimestamp = Date.now();
|
|
31
|
+
settings.sync.flagSpecVersion = '1.2'; // fallback to 1.2 spec
|
|
32
|
+
return fetchSplitChanges(since, noCache, till); // retry request without rbSince
|
|
33
|
+
}
|
|
34
|
+
throw err;
|
|
35
|
+
});
|
|
10
36
|
if (decorator)
|
|
11
37
|
splitsPromise = decorator(splitsPromise);
|
|
12
|
-
return splitsPromise
|
|
38
|
+
return splitsPromise
|
|
39
|
+
.then(function (resp) { return resp.json(); })
|
|
40
|
+
.then(function (data) {
|
|
41
|
+
// Using flag spec version 1.2
|
|
42
|
+
if (data.splits) {
|
|
43
|
+
return {
|
|
44
|
+
ff: {
|
|
45
|
+
d: data.splits,
|
|
46
|
+
s: data.since,
|
|
47
|
+
t: data.till
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Proxy recovery
|
|
52
|
+
if (lastProxyCheckTimestamp) {
|
|
53
|
+
log.info(LOG_PREFIX_SYNC_SPLITS + 'Proxy error recovered');
|
|
54
|
+
lastProxyCheckTimestamp = undefined;
|
|
55
|
+
return splitChangesFetcher(-1, undefined, undefined, -1)
|
|
56
|
+
.then(function (splitChangesResponse) {
|
|
57
|
+
return Promise.all([storage.splits.clear(), storage.rbSegments.clear()])
|
|
58
|
+
.then(function () { return splitChangesResponse; });
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return data;
|
|
62
|
+
});
|
|
13
63
|
};
|
|
14
64
|
}
|
|
@@ -31,10 +31,10 @@ export function pollingManagerCSFactory(params) {
|
|
|
31
31
|
readiness.splits.on(SDK_SPLITS_ARRIVED, function () {
|
|
32
32
|
if (!splitsSyncTask.isRunning())
|
|
33
33
|
return; // noop if not doing polling
|
|
34
|
-
var
|
|
35
|
-
if (
|
|
36
|
-
log.info(POLLING_SMART_PAUSING, [
|
|
37
|
-
if (
|
|
34
|
+
var usingSegments = storage.splits.usesSegments() || storage.rbSegments.usesSegments();
|
|
35
|
+
if (usingSegments !== mySegmentsSyncTask.isRunning()) {
|
|
36
|
+
log.info(POLLING_SMART_PAUSING, [usingSegments ? 'ON' : 'OFF']);
|
|
37
|
+
if (usingSegments) {
|
|
38
38
|
startMySegmentsSyncTasks();
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
@@ -46,10 +46,10 @@ export function pollingManagerCSFactory(params) {
|
|
|
46
46
|
var mySegmentsSyncTask = mySegmentsSyncTaskFactory(splitApi.fetchMemberships, storage, readiness, settings, matchingKey);
|
|
47
47
|
// smart ready
|
|
48
48
|
function smartReady() {
|
|
49
|
-
if (!readiness.isReady() && !storage.splits.usesSegments())
|
|
49
|
+
if (!readiness.isReady() && !storage.splits.usesSegments() && !storage.rbSegments.usesSegments())
|
|
50
50
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
51
51
|
}
|
|
52
|
-
if (!storage.splits.usesSegments())
|
|
52
|
+
if (!storage.splits.usesSegments() && !storage.rbSegments.usesSegments())
|
|
53
53
|
setTimeout(smartReady, 0);
|
|
54
54
|
else
|
|
55
55
|
readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady);
|
|
@@ -63,7 +63,7 @@ export function pollingManagerCSFactory(params) {
|
|
|
63
63
|
start: function () {
|
|
64
64
|
log.info(POLLING_START);
|
|
65
65
|
splitsSyncTask.start();
|
|
66
|
-
if (storage.splits.usesSegments())
|
|
66
|
+
if (storage.splits.usesSegments() || storage.rbSegments.usesSegments())
|
|
67
67
|
startMySegmentsSyncTasks();
|
|
68
68
|
},
|
|
69
69
|
// Stop periodic fetching (polling)
|
|
@@ -5,5 +5,5 @@ import { splitChangesUpdaterFactory } from '../updaters/splitChangesUpdater';
|
|
|
5
5
|
* Creates a sync task that periodically executes a `splitChangesUpdater` task
|
|
6
6
|
*/
|
|
7
7
|
export function splitsSyncTaskFactory(fetchSplitChanges, storage, readiness, settings, isClientSide) {
|
|
8
|
-
return syncTaskFactory(settings.log, splitChangesUpdaterFactory(settings.log, splitChangesFetcherFactory(fetchSplitChanges), storage, settings.sync.__splitFiltersValidation, readiness.splits, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, isClientSide), settings.scheduler.featuresRefreshRate, 'splitChangesUpdater');
|
|
8
|
+
return syncTaskFactory(settings.log, splitChangesUpdaterFactory(settings.log, splitChangesFetcherFactory(fetchSplitChanges, settings, storage), storage, settings.sync.__splitFiltersValidation, readiness.splits, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, isClientSide), settings.scheduler.featuresRefreshRate, 'splitChangesUpdater');
|
|
9
9
|
}
|
|
@@ -9,7 +9,7 @@ import { MEMBERSHIPS_LS_UPDATE } from '../../streaming/constants';
|
|
|
9
9
|
* - uses `segmentsEventEmitter` to emit events related to segments data updates
|
|
10
10
|
*/
|
|
11
11
|
export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, matchingKey) {
|
|
12
|
-
var splits = storage.splits, segments = storage.segments, largeSegments = storage.largeSegments;
|
|
12
|
+
var splits = storage.splits, rbSegments = storage.rbSegments, segments = storage.segments, largeSegments = storage.largeSegments;
|
|
13
13
|
var readyOnAlreadyExistentState = true;
|
|
14
14
|
var startingUp = true;
|
|
15
15
|
/** timeout and telemetry decorator for `splitChangesFetcher` promise */
|
|
@@ -31,7 +31,7 @@ export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmen
|
|
|
31
31
|
shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate;
|
|
32
32
|
}
|
|
33
33
|
// Notify update if required
|
|
34
|
-
if (splits.usesSegments() && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
34
|
+
if ((splits.usesSegments() || rbSegments.usesSegments()) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
35
35
|
readyOnAlreadyExistentState = false;
|
|
36
36
|
segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED);
|
|
37
37
|
}
|
|
@@ -36,7 +36,7 @@ export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segment
|
|
|
36
36
|
* Returned promise will not be rejected.
|
|
37
37
|
*
|
|
38
38
|
* @param fetchOnlyNew - if true, only fetch the segments that not exists, i.e., which `changeNumber` is equal to -1.
|
|
39
|
-
* This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE notifications.
|
|
39
|
+
* This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE or RB_SEGMENT_UPDATE notifications.
|
|
40
40
|
* @param segmentName - segment name to fetch. By passing `undefined` it fetches the list of segments registered at the storage
|
|
41
41
|
* @param noCache - true to revalidate data to fetch on a SEGMENT_UPDATE notifications.
|
|
42
42
|
* @param till - till target for the provided segmentName, for CDN bypass.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { timeout } from '../../../utils/promise/timeout';
|
|
2
2
|
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
3
|
-
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
3
|
+
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_RBS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
4
4
|
import { startsWith } from '../../../utils/lang';
|
|
5
|
-
import { IN_SEGMENT } from '../../../utils/constants';
|
|
5
|
+
import { IN_RULE_BASED_SEGMENT, IN_SEGMENT, RULE_BASED_SEGMENT, STANDARD_SEGMENT } from '../../../utils/constants';
|
|
6
6
|
import { setToArray } from '../../../utils/lang/sets';
|
|
7
|
+
import { SPLIT_UPDATE } from '../../streaming/constants';
|
|
7
8
|
// Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
|
|
8
9
|
// Returns a promise that could be rejected.
|
|
9
10
|
// @TODO review together with Segments and MySegments storage APIs
|
|
@@ -15,16 +16,25 @@ function checkAllSegmentsExist(segments) {
|
|
|
15
16
|
});
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
|
-
* Collect segments from a raw
|
|
19
|
+
* Collect segments from a raw FF or RBS definition.
|
|
19
20
|
* Exported for testing purposes.
|
|
20
21
|
*/
|
|
21
|
-
export function parseSegments(
|
|
22
|
-
|
|
22
|
+
export function parseSegments(ruleEntity, matcherType) {
|
|
23
|
+
if (matcherType === void 0) { matcherType = IN_SEGMENT; }
|
|
24
|
+
var _a = ruleEntity, _b = _a.conditions, conditions = _b === void 0 ? [] : _b, excluded = _a.excluded;
|
|
23
25
|
var segments = new Set();
|
|
26
|
+
if (excluded && excluded.segments) {
|
|
27
|
+
excluded.segments.forEach(function (_a) {
|
|
28
|
+
var type = _a.type, name = _a.name;
|
|
29
|
+
if ((type === STANDARD_SEGMENT && matcherType === IN_SEGMENT) || (type === RULE_BASED_SEGMENT && matcherType === IN_RULE_BASED_SEGMENT)) {
|
|
30
|
+
segments.add(name);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
24
34
|
for (var i = 0; i < conditions.length; i++) {
|
|
25
35
|
var matchers = conditions[i].matcherGroup.matchers;
|
|
26
36
|
matchers.forEach(function (matcher) {
|
|
27
|
-
if (matcher.matcherType ===
|
|
37
|
+
if (matcher.matcherType === matcherType)
|
|
28
38
|
segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
|
|
29
39
|
});
|
|
30
40
|
}
|
|
@@ -54,22 +64,19 @@ function matchFilters(featureFlag, filters) {
|
|
|
54
64
|
* i.e., an object with added splits, removed splits and used segments.
|
|
55
65
|
* Exported for testing purposes.
|
|
56
66
|
*/
|
|
57
|
-
export function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
parseSegments(split).forEach(function (segmentName) {
|
|
67
|
+
export function computeMutation(rules, segments, filters) {
|
|
68
|
+
return rules.reduce(function (accum, ruleEntity) {
|
|
69
|
+
if (ruleEntity.status === 'ACTIVE' && (!filters || matchFilters(ruleEntity, filters))) {
|
|
70
|
+
accum.added.push(ruleEntity);
|
|
71
|
+
parseSegments(ruleEntity).forEach(function (segmentName) {
|
|
63
72
|
segments.add(segmentName);
|
|
64
73
|
});
|
|
65
74
|
}
|
|
66
75
|
else {
|
|
67
|
-
accum.removed.push(
|
|
76
|
+
accum.removed.push(ruleEntity);
|
|
68
77
|
}
|
|
69
78
|
return accum;
|
|
70
|
-
}, { added: [], removed: []
|
|
71
|
-
computed.segments = setToArray(segments);
|
|
72
|
-
return computed;
|
|
79
|
+
}, { added: [], removed: [] });
|
|
73
80
|
}
|
|
74
81
|
/**
|
|
75
82
|
* factory of SplitChanges updater, a task that:
|
|
@@ -88,7 +95,7 @@ export function computeSplitsMutation(entries, filters) {
|
|
|
88
95
|
export function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFiltersValidation, splitsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, isClientSide) {
|
|
89
96
|
if (requestTimeoutBeforeReady === void 0) { requestTimeoutBeforeReady = 0; }
|
|
90
97
|
if (retriesOnFailureBeforeReady === void 0) { retriesOnFailureBeforeReady = 0; }
|
|
91
|
-
var splits = storage.splits, segments = storage.segments;
|
|
98
|
+
var splits = storage.splits, rbSegments = storage.rbSegments, segments = storage.segments;
|
|
92
99
|
var startingUp = true;
|
|
93
100
|
/** timeout decorator for `splitChangesFetcher` promise */
|
|
94
101
|
function _promiseDecorator(promise) {
|
|
@@ -103,29 +110,48 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, sp
|
|
|
103
110
|
* @param noCache - true to revalidate data to fetch
|
|
104
111
|
* @param till - query param to bypass CDN requests
|
|
105
112
|
*/
|
|
106
|
-
return function splitChangesUpdater(noCache, till,
|
|
113
|
+
return function splitChangesUpdater(noCache, till, instantUpdate) {
|
|
107
114
|
/**
|
|
108
115
|
* @param since - current changeNumber at splitsCache
|
|
109
116
|
* @param retry - current number of retry attempts
|
|
110
117
|
*/
|
|
111
|
-
function _splitChangesUpdater(
|
|
118
|
+
function _splitChangesUpdater(sinces, retry) {
|
|
112
119
|
if (retry === void 0) { retry = 0; }
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
var since = sinces[0], rbSince = sinces[1];
|
|
121
|
+
log.debug(SYNC_SPLITS_FETCH, sinces);
|
|
122
|
+
return Promise.resolve(instantUpdate ?
|
|
123
|
+
instantUpdate.type === SPLIT_UPDATE ?
|
|
124
|
+
// IFFU edge case: a change to a flag that adds an IN_RULE_BASED_SEGMENT matcher that is not present yet
|
|
125
|
+
Promise.resolve(rbSegments.contains(parseSegments(instantUpdate.payload, IN_RULE_BASED_SEGMENT))).then(function (contains) {
|
|
126
|
+
return contains ?
|
|
127
|
+
{ ff: { d: [instantUpdate.payload], t: instantUpdate.changeNumber } } :
|
|
128
|
+
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator);
|
|
129
|
+
}) :
|
|
130
|
+
{ rbs: { d: [instantUpdate.payload], t: instantUpdate.changeNumber } } :
|
|
131
|
+
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator))
|
|
117
132
|
.then(function (splitChanges) {
|
|
118
133
|
startingUp = false;
|
|
119
|
-
var
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
134
|
+
var usedSegments = new Set();
|
|
135
|
+
var ffUpdate = false;
|
|
136
|
+
if (splitChanges.ff) {
|
|
137
|
+
var _a = computeMutation(splitChanges.ff.d, usedSegments, splitFiltersValidation), added = _a.added, removed = _a.removed;
|
|
138
|
+
log.debug(SYNC_SPLITS_UPDATE, [added.length, removed.length]);
|
|
139
|
+
ffUpdate = splits.update(added, removed, splitChanges.ff.t);
|
|
140
|
+
}
|
|
141
|
+
var rbsUpdate = false;
|
|
142
|
+
if (splitChanges.rbs) {
|
|
143
|
+
var _b = computeMutation(splitChanges.rbs.d, usedSegments), added = _b.added, removed = _b.removed;
|
|
144
|
+
log.debug(SYNC_RBS_UPDATE, [added.length, removed.length]);
|
|
145
|
+
rbsUpdate = rbSegments.update(added, removed, splitChanges.rbs.t);
|
|
146
|
+
}
|
|
147
|
+
return Promise.all([ffUpdate, rbsUpdate,
|
|
148
|
+
// @TODO if at least 1 segment fetch fails due to 404 and other segments are updated in the storage, SDK_UPDATE is not emitted
|
|
149
|
+
segments.registerSegments(setToArray(usedSegments))
|
|
124
150
|
]).then(function (_a) {
|
|
125
|
-
var
|
|
151
|
+
var ffChanged = _a[0], rbsChanged = _a[1];
|
|
126
152
|
if (splitsEventEmitter) {
|
|
127
153
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
128
|
-
return Promise.resolve(!splitsEventEmitter.splitsArrived || (
|
|
154
|
+
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
129
155
|
.catch(function () { return false; } /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
130
156
|
.then(function (emitSplitsArrivedEvent) {
|
|
131
157
|
// emit SDK events
|
|
@@ -142,7 +168,7 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, sp
|
|
|
142
168
|
if (startingUp && retriesOnFailureBeforeReady > retry) {
|
|
143
169
|
retry += 1;
|
|
144
170
|
log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
|
|
145
|
-
return _splitChangesUpdater(
|
|
171
|
+
return _splitChangesUpdater(sinces, retry);
|
|
146
172
|
}
|
|
147
173
|
else {
|
|
148
174
|
startingUp = false;
|
|
@@ -150,7 +176,7 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, sp
|
|
|
150
176
|
return false;
|
|
151
177
|
});
|
|
152
178
|
}
|
|
153
|
-
|
|
154
|
-
return
|
|
179
|
+
// `getChangeNumber` never rejects or throws error
|
|
180
|
+
return Promise.all([splits.getChangeNumber(), rbSegments.getChangeNumber()]).then(_splitChangesUpdater);
|
|
155
181
|
};
|
|
156
182
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { errorParser, messageParser } from './NotificationParser';
|
|
2
2
|
import { notificationKeeperFactory } from './NotificationKeeper';
|
|
3
|
-
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE } from '../constants';
|
|
3
|
+
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, RB_SEGMENT_UPDATE } from '../constants';
|
|
4
4
|
import { STREAMING_PARSING_ERROR_FAILS, ERROR_STREAMING_SSE, STREAMING_PARSING_MESSAGE_FAILS, STREAMING_NEW_MESSAGE } from '../../../logger/constants';
|
|
5
5
|
import { ABLY_ERROR, NON_REQUESTED, SSE_CONNECTION_ERROR } from '../../../utils/constants';
|
|
6
6
|
/**
|
|
@@ -75,6 +75,7 @@ export function SSEHandlerFactory(log, pushEmitter, telemetryTracker) {
|
|
|
75
75
|
case MEMBERSHIPS_MS_UPDATE:
|
|
76
76
|
case MEMBERSHIPS_LS_UPDATE:
|
|
77
77
|
case SPLIT_KILL:
|
|
78
|
+
case RB_SEGMENT_UPDATE:
|
|
78
79
|
pushEmitter.emit(parsedData.type, parsedData);
|
|
79
80
|
break;
|
|
80
81
|
/* occupancy & control events, handled by NotificationManagerKeeper */
|