@splitsoftware/splitio-commons 2.2.0 → 2.2.1-rc.1

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.
Files changed (140) hide show
  1. package/CHANGES.txt +3 -0
  2. package/README.md +1 -0
  3. package/cjs/evaluator/combiners/and.js +2 -6
  4. package/cjs/evaluator/combiners/ifelseif.js +6 -6
  5. package/cjs/evaluator/condition/index.js +6 -5
  6. package/cjs/evaluator/index.js +7 -7
  7. package/cjs/evaluator/matchers/index.js +3 -1
  8. package/cjs/evaluator/matchers/matcherTypes.js +1 -0
  9. package/cjs/evaluator/matchers/rbsegment.js +44 -0
  10. package/cjs/evaluator/matchersTransform/index.js +4 -0
  11. package/cjs/evaluator/parser/index.js +2 -2
  12. package/cjs/evaluator/value/sanitize.js +1 -0
  13. package/cjs/logger/constants.js +4 -3
  14. package/cjs/logger/messages/debug.js +3 -2
  15. package/cjs/logger/messages/warn.js +1 -1
  16. package/cjs/services/splitApi.js +3 -4
  17. package/cjs/storages/AbstractSplitsCacheSync.js +5 -2
  18. package/cjs/storages/KeyBuilder.js +9 -0
  19. package/cjs/storages/KeyBuilderCS.js +3 -0
  20. package/cjs/storages/KeyBuilderSS.js +3 -0
  21. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +117 -0
  22. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +9 -14
  23. package/cjs/storages/inLocalStorage/index.js +5 -1
  24. package/cjs/storages/inLocalStorage/validateCache.js +2 -1
  25. package/cjs/storages/inMemory/InMemoryStorage.js +3 -0
  26. package/cjs/storages/inMemory/InMemoryStorageCS.js +4 -0
  27. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +61 -0
  28. package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +64 -0
  29. package/cjs/storages/inRedis/index.js +2 -0
  30. package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +64 -0
  31. package/cjs/storages/pluggable/index.js +2 -0
  32. package/cjs/sync/polling/fetchers/splitChangesFetcher.js +51 -4
  33. package/cjs/sync/polling/pollingManagerCS.js +7 -7
  34. package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
  35. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  36. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  37. package/cjs/sync/polling/updaters/splitChangesUpdater.js +51 -33
  38. package/cjs/sync/streaming/SSEHandler/index.js +1 -0
  39. package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +106 -77
  40. package/cjs/sync/streaming/constants.js +2 -1
  41. package/cjs/sync/streaming/pushManager.js +3 -16
  42. package/cjs/sync/syncManagerOnline.js +2 -2
  43. package/cjs/utils/constants/index.js +3 -2
  44. package/esm/evaluator/combiners/and.js +2 -6
  45. package/esm/evaluator/combiners/ifelseif.js +7 -7
  46. package/esm/evaluator/condition/index.js +6 -5
  47. package/esm/evaluator/index.js +7 -7
  48. package/esm/evaluator/matchers/index.js +3 -1
  49. package/esm/evaluator/matchers/matcherTypes.js +1 -0
  50. package/esm/evaluator/matchers/rbsegment.js +40 -0
  51. package/esm/evaluator/matchersTransform/index.js +4 -0
  52. package/esm/evaluator/parser/index.js +2 -2
  53. package/esm/evaluator/value/sanitize.js +1 -0
  54. package/esm/logger/constants.js +1 -0
  55. package/esm/logger/messages/debug.js +3 -2
  56. package/esm/logger/messages/warn.js +1 -1
  57. package/esm/services/splitApi.js +3 -4
  58. package/esm/storages/AbstractSplitsCacheSync.js +5 -2
  59. package/esm/storages/KeyBuilder.js +9 -0
  60. package/esm/storages/KeyBuilderCS.js +3 -0
  61. package/esm/storages/KeyBuilderSS.js +3 -0
  62. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +114 -0
  63. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +9 -14
  64. package/esm/storages/inLocalStorage/index.js +5 -1
  65. package/esm/storages/inLocalStorage/validateCache.js +2 -1
  66. package/esm/storages/inMemory/InMemoryStorage.js +3 -0
  67. package/esm/storages/inMemory/InMemoryStorageCS.js +4 -0
  68. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +58 -0
  69. package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +61 -0
  70. package/esm/storages/inRedis/index.js +2 -0
  71. package/esm/storages/pluggable/RBSegmentsCachePluggable.js +61 -0
  72. package/esm/storages/pluggable/index.js +2 -0
  73. package/esm/sync/polling/fetchers/splitChangesFetcher.js +51 -4
  74. package/esm/sync/polling/pollingManagerCS.js +7 -7
  75. package/esm/sync/polling/syncTasks/splitsSyncTask.js +1 -1
  76. package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  77. package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  78. package/esm/sync/polling/updaters/splitChangesUpdater.js +51 -33
  79. package/esm/sync/streaming/SSEHandler/index.js +2 -1
  80. package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +102 -73
  81. package/esm/sync/streaming/constants.js +1 -0
  82. package/esm/sync/streaming/pushManager.js +6 -19
  83. package/esm/sync/syncManagerOnline.js +2 -2
  84. package/esm/utils/constants/index.js +2 -1
  85. package/package.json +1 -1
  86. package/src/dtos/types.ts +32 -8
  87. package/src/evaluator/Engine.ts +1 -1
  88. package/src/evaluator/combiners/and.ts +5 -4
  89. package/src/evaluator/combiners/ifelseif.ts +7 -9
  90. package/src/evaluator/condition/engineUtils.ts +1 -1
  91. package/src/evaluator/condition/index.ts +12 -12
  92. package/src/evaluator/index.ts +7 -7
  93. package/src/evaluator/matchers/index.ts +3 -1
  94. package/src/evaluator/matchers/matcherTypes.ts +1 -0
  95. package/src/evaluator/matchers/rbsegment.ts +62 -0
  96. package/src/evaluator/matchersTransform/index.ts +3 -0
  97. package/src/evaluator/parser/index.ts +3 -3
  98. package/src/evaluator/types.ts +2 -2
  99. package/src/evaluator/value/index.ts +2 -2
  100. package/src/evaluator/value/sanitize.ts +5 -4
  101. package/src/logger/constants.ts +1 -0
  102. package/src/logger/messages/debug.ts +3 -2
  103. package/src/logger/messages/warn.ts +1 -1
  104. package/src/sdkManager/index.ts +1 -1
  105. package/src/services/splitApi.ts +3 -4
  106. package/src/services/types.ts +1 -1
  107. package/src/storages/AbstractSplitsCacheSync.ts +6 -3
  108. package/src/storages/KeyBuilder.ts +12 -0
  109. package/src/storages/KeyBuilderCS.ts +4 -0
  110. package/src/storages/KeyBuilderSS.ts +4 -0
  111. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +136 -0
  112. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +10 -14
  113. package/src/storages/inLocalStorage/index.ts +5 -1
  114. package/src/storages/inLocalStorage/validateCache.ts +3 -1
  115. package/src/storages/inMemory/InMemoryStorage.ts +3 -0
  116. package/src/storages/inMemory/InMemoryStorageCS.ts +4 -0
  117. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +68 -0
  118. package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +79 -0
  119. package/src/storages/inRedis/index.ts +2 -0
  120. package/src/storages/pluggable/RBSegmentsCachePluggable.ts +76 -0
  121. package/src/storages/pluggable/index.ts +2 -0
  122. package/src/storages/types.ts +33 -1
  123. package/src/sync/polling/fetchers/splitChangesFetcher.ts +62 -4
  124. package/src/sync/polling/fetchers/types.ts +1 -0
  125. package/src/sync/polling/pollingManagerCS.ts +7 -7
  126. package/src/sync/polling/syncTasks/splitsSyncTask.ts +1 -1
  127. package/src/sync/polling/types.ts +2 -2
  128. package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -2
  129. package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
  130. package/src/sync/polling/updaters/splitChangesUpdater.ts +61 -42
  131. package/src/sync/streaming/SSEHandler/index.ts +2 -1
  132. package/src/sync/streaming/SSEHandler/types.ts +2 -2
  133. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +98 -68
  134. package/src/sync/streaming/constants.ts +1 -0
  135. package/src/sync/streaming/parseUtils.ts +2 -2
  136. package/src/sync/streaming/pushManager.ts +6 -18
  137. package/src/sync/streaming/types.ts +3 -2
  138. package/src/sync/syncManagerOnline.ts +2 -2
  139. package/src/utils/constants/index.ts +2 -1
  140. package/src/utils/lang/index.ts +1 -1
@@ -9,6 +9,7 @@ var ImpressionCountsCacheInMemory_1 = require("./ImpressionCountsCacheInMemory")
9
9
  var constants_1 = require("../../utils/constants");
10
10
  var TelemetryCacheInMemory_1 = require("./TelemetryCacheInMemory");
11
11
  var UniqueKeysCacheInMemoryCS_1 = require("./UniqueKeysCacheInMemoryCS");
12
+ var RBSegmentsCacheInMemory_1 = require("./RBSegmentsCacheInMemory");
12
13
  /**
13
14
  * InMemory storage factory for standalone client-side SplitFactory
14
15
  *
@@ -17,10 +18,12 @@ var UniqueKeysCacheInMemoryCS_1 = require("./UniqueKeysCacheInMemoryCS");
17
18
  function InMemoryStorageCSFactory(params) {
18
19
  var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, __splitFiltersValidation = _a.sync.__splitFiltersValidation;
19
20
  var splits = new SplitsCacheInMemory_1.SplitsCacheInMemory(__splitFiltersValidation);
21
+ var rbSegments = new RBSegmentsCacheInMemory_1.RBSegmentsCacheInMemory();
20
22
  var segments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
21
23
  var largeSegments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
22
24
  var storage = {
23
25
  splits: splits,
26
+ rbSegments: rbSegments,
24
27
  segments: segments,
25
28
  largeSegments: largeSegments,
26
29
  impressions: new ImpressionsCacheInMemory_1.ImpressionsCacheInMemory(impressionsQueueSize),
@@ -33,6 +36,7 @@ function InMemoryStorageCSFactory(params) {
33
36
  shared: function () {
34
37
  return {
35
38
  splits: this.splits,
39
+ rbSegments: this.rbSegments,
36
40
  segments: new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory(),
37
41
  largeSegments: new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory(),
38
42
  impressions: this.impressions,
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RBSegmentsCacheInMemory = void 0;
4
+ var sets_1 = require("../../utils/lang/sets");
5
+ var AbstractSplitsCacheSync_1 = require("../AbstractSplitsCacheSync");
6
+ var RBSegmentsCacheInMemory = /** @class */ (function () {
7
+ function RBSegmentsCacheInMemory() {
8
+ this.cache = {};
9
+ this.changeNumber = -1;
10
+ this.segmentsCount = 0;
11
+ }
12
+ RBSegmentsCacheInMemory.prototype.clear = function () {
13
+ this.cache = {};
14
+ this.changeNumber = -1;
15
+ this.segmentsCount = 0;
16
+ };
17
+ RBSegmentsCacheInMemory.prototype.update = function (toAdd, toRemove, changeNumber) {
18
+ var _this = this;
19
+ this.changeNumber = changeNumber;
20
+ var updated = toAdd.map(function (toAdd) { return _this.add(toAdd); }).some(function (result) { return result; });
21
+ return toRemove.map(function (toRemove) { return _this.remove(toRemove.name); }).some(function (result) { return result; }) || updated;
22
+ };
23
+ RBSegmentsCacheInMemory.prototype.add = function (rbSegment) {
24
+ var name = rbSegment.name;
25
+ var previous = this.get(name);
26
+ if (previous && (0, AbstractSplitsCacheSync_1.usesSegments)(previous))
27
+ this.segmentsCount--;
28
+ this.cache[name] = rbSegment;
29
+ if ((0, AbstractSplitsCacheSync_1.usesSegments)(rbSegment))
30
+ this.segmentsCount++;
31
+ return true;
32
+ };
33
+ RBSegmentsCacheInMemory.prototype.remove = function (name) {
34
+ var rbSegment = this.get(name);
35
+ if (!rbSegment)
36
+ return false;
37
+ delete this.cache[name];
38
+ if ((0, AbstractSplitsCacheSync_1.usesSegments)(rbSegment))
39
+ this.segmentsCount--;
40
+ return true;
41
+ };
42
+ RBSegmentsCacheInMemory.prototype.getNames = function () {
43
+ return Object.keys(this.cache);
44
+ };
45
+ RBSegmentsCacheInMemory.prototype.get = function (name) {
46
+ return this.cache[name] || null;
47
+ };
48
+ RBSegmentsCacheInMemory.prototype.contains = function (names) {
49
+ var namesArray = (0, sets_1.setToArray)(names);
50
+ var namesInStorage = this.getNames();
51
+ return namesArray.every(function (name) { return namesInStorage.indexOf(name) !== -1; });
52
+ };
53
+ RBSegmentsCacheInMemory.prototype.getChangeNumber = function () {
54
+ return this.changeNumber;
55
+ };
56
+ RBSegmentsCacheInMemory.prototype.usesSegments = function () {
57
+ return this.segmentsCount > 0;
58
+ };
59
+ return RBSegmentsCacheInMemory;
60
+ }());
61
+ exports.RBSegmentsCacheInMemory = RBSegmentsCacheInMemory;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RBSegmentsCacheInRedis = void 0;
4
+ var lang_1 = require("../../utils/lang");
5
+ var constants_1 = require("./constants");
6
+ var sets_1 = require("../../utils/lang/sets");
7
+ var RBSegmentsCacheInRedis = /** @class */ (function () {
8
+ function RBSegmentsCacheInRedis(log, keys, redis) {
9
+ this.log = log;
10
+ this.keys = keys;
11
+ this.redis = redis;
12
+ }
13
+ RBSegmentsCacheInRedis.prototype.get = function (name) {
14
+ return this.redis.get(this.keys.buildRBSegmentKey(name))
15
+ .then(function (maybeRBSegment) { return maybeRBSegment && JSON.parse(maybeRBSegment); });
16
+ };
17
+ RBSegmentsCacheInRedis.prototype.getNames = function () {
18
+ var _this = this;
19
+ return this.redis.keys(this.keys.searchPatternForRBSegmentKeys()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
20
+ };
21
+ RBSegmentsCacheInRedis.prototype.contains = function (names) {
22
+ var namesArray = (0, sets_1.setToArray)(names);
23
+ return this.getNames().then(function (namesInStorage) {
24
+ return namesArray.every(function (name) { return namesInStorage.includes(name); });
25
+ });
26
+ };
27
+ RBSegmentsCacheInRedis.prototype.update = function (toAdd, toRemove, changeNumber) {
28
+ var _this = this;
29
+ return Promise.all([
30
+ this.setChangeNumber(changeNumber),
31
+ Promise.all(toAdd.map(function (toAdd) {
32
+ var key = _this.keys.buildRBSegmentKey(toAdd.name);
33
+ var stringifiedNewRBSegment = JSON.stringify(toAdd);
34
+ return _this.redis.set(key, stringifiedNewRBSegment).then(function () { return true; });
35
+ })),
36
+ Promise.all(toRemove.map(function (toRemove) {
37
+ var key = _this.keys.buildRBSegmentKey(toRemove.name);
38
+ return _this.redis.del(key).then(function (status) { return status === 1; });
39
+ }))
40
+ ]).then(function (_a) {
41
+ var added = _a[1], removed = _a[2];
42
+ return added.some(function (result) { return result; }) || removed.some(function (result) { return result; });
43
+ });
44
+ };
45
+ RBSegmentsCacheInRedis.prototype.setChangeNumber = function (changeNumber) {
46
+ return this.redis.set(this.keys.buildRBSegmentsTillKey(), changeNumber + '').then(function (status) { return status === 'OK'; });
47
+ };
48
+ RBSegmentsCacheInRedis.prototype.getChangeNumber = function () {
49
+ var _this = this;
50
+ return this.redis.get(this.keys.buildRBSegmentsTillKey()).then(function (value) {
51
+ var i = parseInt(value, 10);
52
+ return (0, lang_1.isNaNNumber)(i) ? -1 : i;
53
+ }).catch(function (e) {
54
+ _this.log.error(constants_1.LOG_PREFIX + 'Could not retrieve changeNumber from storage. Error: ' + e);
55
+ return -1;
56
+ });
57
+ };
58
+ // @TODO implement if required by DataLoader or producer mode
59
+ RBSegmentsCacheInRedis.prototype.clear = function () {
60
+ return Promise.resolve();
61
+ };
62
+ return RBSegmentsCacheInRedis;
63
+ }());
64
+ exports.RBSegmentsCacheInRedis = RBSegmentsCacheInRedis;
@@ -12,6 +12,7 @@ var TelemetryCacheInRedis_1 = require("./TelemetryCacheInRedis");
12
12
  var UniqueKeysCacheInRedis_1 = require("./UniqueKeysCacheInRedis");
13
13
  var ImpressionCountsCacheInRedis_1 = require("./ImpressionCountsCacheInRedis");
14
14
  var utils_1 = require("../utils");
15
+ var RBSegmentsCacheInRedis_1 = require("./RBSegmentsCacheInRedis");
15
16
  /**
16
17
  * InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.js
17
18
  * @see {@link https://www.npmjs.com/package/ioredis}
@@ -40,6 +41,7 @@ function InRedisStorage(options) {
40
41
  });
41
42
  return {
42
43
  splits: new SplitsCacheInRedis_1.SplitsCacheInRedis(log, keys, redisClient, settings.sync.__splitFiltersValidation),
44
+ rbSegments: new RBSegmentsCacheInRedis_1.RBSegmentsCacheInRedis(log, keys, redisClient),
43
45
  segments: new SegmentsCacheInRedis_1.SegmentsCacheInRedis(log, keys, redisClient),
44
46
  impressions: new ImpressionsCacheInRedis_1.ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
45
47
  impressionCounts: impressionCountsCache,
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RBSegmentsCachePluggable = void 0;
4
+ var lang_1 = require("../../utils/lang");
5
+ var constants_1 = require("./constants");
6
+ var sets_1 = require("../../utils/lang/sets");
7
+ var RBSegmentsCachePluggable = /** @class */ (function () {
8
+ function RBSegmentsCachePluggable(log, keys, wrapper) {
9
+ this.log = log;
10
+ this.keys = keys;
11
+ this.wrapper = wrapper;
12
+ }
13
+ RBSegmentsCachePluggable.prototype.get = function (name) {
14
+ return this.wrapper.get(this.keys.buildRBSegmentKey(name))
15
+ .then(function (maybeRBSegment) { return maybeRBSegment && JSON.parse(maybeRBSegment); });
16
+ };
17
+ RBSegmentsCachePluggable.prototype.getNames = function () {
18
+ var _this = this;
19
+ return this.wrapper.getKeysByPrefix(this.keys.buildRBSegmentKeyPrefix()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
20
+ };
21
+ RBSegmentsCachePluggable.prototype.contains = function (names) {
22
+ var namesArray = (0, sets_1.setToArray)(names);
23
+ return this.getNames().then(function (namesInStorage) {
24
+ return namesArray.every(function (name) { return namesInStorage.includes(name); });
25
+ });
26
+ };
27
+ RBSegmentsCachePluggable.prototype.update = function (toAdd, toRemove, changeNumber) {
28
+ var _this = this;
29
+ return Promise.all([
30
+ this.setChangeNumber(changeNumber),
31
+ Promise.all(toAdd.map(function (toAdd) {
32
+ var key = _this.keys.buildRBSegmentKey(toAdd.name);
33
+ var stringifiedNewRBSegment = JSON.stringify(toAdd);
34
+ return _this.wrapper.set(key, stringifiedNewRBSegment).then(function () { return true; });
35
+ })),
36
+ Promise.all(toRemove.map(function (toRemove) {
37
+ var key = _this.keys.buildRBSegmentKey(toRemove.name);
38
+ return _this.wrapper.del(key);
39
+ }))
40
+ ]).then(function (_a) {
41
+ var added = _a[1], removed = _a[2];
42
+ return added.some(function (result) { return result; }) || removed.some(function (result) { return result; });
43
+ });
44
+ };
45
+ RBSegmentsCachePluggable.prototype.setChangeNumber = function (changeNumber) {
46
+ return this.wrapper.set(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
47
+ };
48
+ RBSegmentsCachePluggable.prototype.getChangeNumber = function () {
49
+ var _this = this;
50
+ return this.wrapper.get(this.keys.buildRBSegmentsTillKey()).then(function (value) {
51
+ var i = parseInt(value, 10);
52
+ return (0, lang_1.isNaNNumber)(i) ? -1 : i;
53
+ }).catch(function (e) {
54
+ _this.log.error(constants_1.LOG_PREFIX + 'Could not retrieve changeNumber from storage. Error: ' + e);
55
+ return -1;
56
+ });
57
+ };
58
+ // @TODO implement if required by DataLoader or producer mode
59
+ RBSegmentsCachePluggable.prototype.clear = function () {
60
+ return Promise.resolve();
61
+ };
62
+ return RBSegmentsCachePluggable;
63
+ }());
64
+ exports.RBSegmentsCachePluggable = RBSegmentsCachePluggable;
@@ -22,6 +22,7 @@ var UniqueKeysCacheInMemory_1 = require("../inMemory/UniqueKeysCacheInMemory");
22
22
  var UniqueKeysCacheInMemoryCS_1 = require("../inMemory/UniqueKeysCacheInMemoryCS");
23
23
  var utils_1 = require("../utils");
24
24
  var constants_2 = require("../pluggable/constants");
25
+ var RBSegmentsCachePluggable_1 = require("./RBSegmentsCachePluggable");
25
26
  var NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
26
27
  var NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
27
28
  /**
@@ -105,6 +106,7 @@ function PluggableStorage(options) {
105
106
  });
106
107
  return {
107
108
  splits: new SplitsCachePluggable_1.SplitsCachePluggable(log, keys, wrapper, settings.sync.__splitFiltersValidation),
109
+ rbSegments: new RBSegmentsCachePluggable_1.RBSegmentsCachePluggable(log, keys, wrapper),
108
110
  segments: new SegmentsCachePluggable_1.SegmentsCachePluggable(log, keys, wrapper),
109
111
  impressions: isPartialConsumer ? new ImpressionsCacheInMemory_1.ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable_1.ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
110
112
  impressionCounts: impressionCountsCache,
@@ -1,18 +1,65 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.splitChangesFetcherFactory = void 0;
4
+ var constants_1 = require("../../../utils/constants");
5
+ var settingsValidation_1 = require("../../../utils/settingsValidation");
6
+ var constants_2 = require("../../../logger/constants");
7
+ var PROXY_CHECK_INTERVAL_MILLIS_CS = 60 * 60 * 1000; // 1 hour in Client Side
8
+ var PROXY_CHECK_INTERVAL_MILLIS_SS = 24 * PROXY_CHECK_INTERVAL_MILLIS_CS; // 24 hours in Server Side
9
+ function sdkEndpointOverriden(settings) {
10
+ return settings.urls.sdk !== settingsValidation_1.base.urls.sdk;
11
+ }
4
12
  /**
5
13
  * Factory of SplitChanges fetcher.
6
14
  * SplitChanges fetcher is a wrapper around `splitChanges` API service that parses the response and handle errors.
7
15
  */
8
- function splitChangesFetcherFactory(fetchSplitChanges) {
9
- return function splitChangesFetcher(since, noCache, till,
16
+ // @TODO breaking: drop support for Split Proxy below v5.10.0 and simplify the implementation
17
+ function splitChangesFetcherFactory(fetchSplitChanges, settings, storage) {
18
+ var log = settings.log;
19
+ var PROXY_CHECK_INTERVAL_MILLIS = settings.core.key !== undefined ? PROXY_CHECK_INTERVAL_MILLIS_CS : PROXY_CHECK_INTERVAL_MILLIS_SS;
20
+ var lastProxyCheckTimestamp;
21
+ return function splitChangesFetcher(since, noCache, till, rbSince,
10
22
  // Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
11
23
  decorator) {
12
- var splitsPromise = fetchSplitChanges(since, noCache, till);
24
+ // Recheck proxy
25
+ if (lastProxyCheckTimestamp && (Date.now() - lastProxyCheckTimestamp) > PROXY_CHECK_INTERVAL_MILLIS) {
26
+ settings.sync.flagSpecVersion = constants_1.FLAG_SPEC_VERSION;
27
+ }
28
+ var splitsPromise = fetchSplitChanges(since, noCache, till, settings.sync.flagSpecVersion === constants_1.FLAG_SPEC_VERSION ? rbSince : undefined)
29
+ // Handle proxy error with spec 1.3
30
+ .catch(function (err) {
31
+ if (err.statusCode === 400 && sdkEndpointOverriden(settings) && settings.sync.flagSpecVersion === constants_1.FLAG_SPEC_VERSION) {
32
+ log.error(constants_2.LOG_PREFIX_SYNC_SPLITS + 'Proxy error detected. If you are using Split Proxy, please upgrade to latest version');
33
+ lastProxyCheckTimestamp = Date.now();
34
+ settings.sync.flagSpecVersion = '1.2'; // fallback to 1.2 spec
35
+ return fetchSplitChanges(since, noCache, till); // retry request without rbSince
36
+ }
37
+ throw err;
38
+ });
13
39
  if (decorator)
14
40
  splitsPromise = decorator(splitsPromise);
15
- return splitsPromise.then(function (resp) { return resp.json(); });
41
+ return splitsPromise
42
+ .then(function (resp) { return resp.json(); })
43
+ .then(function (data) {
44
+ // Using flag spec version 1.2
45
+ if (data.splits) {
46
+ return {
47
+ ff: {
48
+ d: data.splits,
49
+ s: data.since,
50
+ t: data.till
51
+ }
52
+ };
53
+ }
54
+ // Proxy recovery
55
+ if (lastProxyCheckTimestamp) {
56
+ log.info(constants_2.LOG_PREFIX_SYNC_SPLITS + 'Proxy error recovered');
57
+ lastProxyCheckTimestamp = undefined;
58
+ return Promise.all([storage.splits.clear(), storage.rbSegments.clear()])
59
+ .then(function () { return splitChangesFetcher(storage.splits.getChangeNumber(), undefined, undefined, storage.rbSegments.getChangeNumber()); });
60
+ }
61
+ return data;
62
+ });
16
63
  };
17
64
  }
18
65
  exports.splitChangesFetcherFactory = splitChangesFetcherFactory;
@@ -34,10 +34,10 @@ function pollingManagerCSFactory(params) {
34
34
  readiness.splits.on(constants_1.SDK_SPLITS_ARRIVED, function () {
35
35
  if (!splitsSyncTask.isRunning())
36
36
  return; // noop if not doing polling
37
- var splitsHaveSegments = storage.splits.usesSegments();
38
- if (splitsHaveSegments !== mySegmentsSyncTask.isRunning()) {
39
- log.info(constants_2.POLLING_SMART_PAUSING, [splitsHaveSegments ? 'ON' : 'OFF']);
40
- if (splitsHaveSegments) {
37
+ var usingSegments = storage.splits.usesSegments() || storage.rbSegments.usesSegments();
38
+ if (usingSegments !== mySegmentsSyncTask.isRunning()) {
39
+ log.info(constants_2.POLLING_SMART_PAUSING, [usingSegments ? 'ON' : 'OFF']);
40
+ if (usingSegments) {
41
41
  startMySegmentsSyncTasks();
42
42
  }
43
43
  else {
@@ -49,10 +49,10 @@ function pollingManagerCSFactory(params) {
49
49
  var mySegmentsSyncTask = (0, mySegmentsSyncTask_1.mySegmentsSyncTaskFactory)(splitApi.fetchMemberships, storage, readiness, settings, matchingKey);
50
50
  // smart ready
51
51
  function smartReady() {
52
- if (!readiness.isReady() && !storage.splits.usesSegments())
52
+ if (!readiness.isReady() && !storage.splits.usesSegments() && !storage.rbSegments.usesSegments())
53
53
  readiness.segments.emit(constants_1.SDK_SEGMENTS_ARRIVED);
54
54
  }
55
- if (!storage.splits.usesSegments())
55
+ if (!storage.splits.usesSegments() && !storage.rbSegments.usesSegments())
56
56
  setTimeout(smartReady, 0);
57
57
  else
58
58
  readiness.splits.once(constants_1.SDK_SPLITS_ARRIVED, smartReady);
@@ -66,7 +66,7 @@ function pollingManagerCSFactory(params) {
66
66
  start: function () {
67
67
  log.info(constants_2.POLLING_START);
68
68
  splitsSyncTask.start();
69
- if (storage.splits.usesSegments())
69
+ if (storage.splits.usesSegments() || storage.rbSegments.usesSegments())
70
70
  startMySegmentsSyncTasks();
71
71
  },
72
72
  // Stop periodic fetching (polling)
@@ -8,6 +8,6 @@ var splitChangesUpdater_1 = require("../updaters/splitChangesUpdater");
8
8
  * Creates a sync task that periodically executes a `splitChangesUpdater` task
9
9
  */
10
10
  function splitsSyncTaskFactory(fetchSplitChanges, storage, readiness, settings, isClientSide) {
11
- return (0, syncTask_1.syncTaskFactory)(settings.log, (0, splitChangesUpdater_1.splitChangesUpdaterFactory)(settings.log, (0, splitChangesFetcher_1.splitChangesFetcherFactory)(fetchSplitChanges), storage, settings.sync.__splitFiltersValidation, readiness.splits, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, isClientSide), settings.scheduler.featuresRefreshRate, 'splitChangesUpdater');
11
+ return (0, syncTask_1.syncTaskFactory)(settings.log, (0, splitChangesUpdater_1.splitChangesUpdaterFactory)(settings.log, (0, splitChangesFetcher_1.splitChangesFetcherFactory)(fetchSplitChanges, settings, storage), storage, settings.sync.__splitFiltersValidation, readiness.splits, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, isClientSide), settings.scheduler.featuresRefreshRate, 'splitChangesUpdater');
12
12
  }
13
13
  exports.splitsSyncTaskFactory = splitsSyncTaskFactory;
@@ -12,7 +12,7 @@ var constants_3 = require("../../streaming/constants");
12
12
  * - uses `segmentsEventEmitter` to emit events related to segments data updates
13
13
  */
14
14
  function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, matchingKey) {
15
- var splits = storage.splits, segments = storage.segments, largeSegments = storage.largeSegments;
15
+ var splits = storage.splits, rbSegments = storage.rbSegments, segments = storage.segments, largeSegments = storage.largeSegments;
16
16
  var readyOnAlreadyExistentState = true;
17
17
  var startingUp = true;
18
18
  /** timeout and telemetry decorator for `splitChangesFetcher` promise */
@@ -34,7 +34,7 @@ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEvent
34
34
  shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate;
35
35
  }
36
36
  // Notify update if required
37
- if (splits.usesSegments() && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
37
+ if ((splits.usesSegments() || rbSegments.usesSegments()) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
38
38
  readyOnAlreadyExistentState = false;
39
39
  segmentsEventEmitter.emit(constants_1.SDK_SEGMENTS_ARRIVED);
40
40
  }
@@ -39,7 +39,7 @@ function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, read
39
39
  * Returned promise will not be rejected.
40
40
  *
41
41
  * @param fetchOnlyNew - if true, only fetch the segments that not exists, i.e., which `changeNumber` is equal to -1.
42
- * This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE notifications.
42
+ * This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE or RB_SEGMENT_UPDATE notifications.
43
43
  * @param segmentName - segment name to fetch. By passing `undefined` it fetches the list of segments registered at the storage
44
44
  * @param noCache - true to revalidate data to fetch on a SEGMENT_UPDATE notifications.
45
45
  * @param till - till target for the provided segmentName, for CDN bypass.
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.splitChangesUpdaterFactory = exports.computeSplitsMutation = exports.parseSegments = void 0;
3
+ exports.splitChangesUpdaterFactory = exports.computeMutation = exports.parseSegments = void 0;
4
4
  var timeout_1 = require("../../../utils/promise/timeout");
5
5
  var constants_1 = require("../../../readiness/constants");
6
6
  var constants_2 = require("../../../logger/constants");
7
7
  var lang_1 = require("../../../utils/lang");
8
8
  var constants_3 = require("../../../utils/constants");
9
9
  var sets_1 = require("../../../utils/lang/sets");
10
+ var constants_4 = require("../../streaming/constants");
10
11
  // Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
11
12
  // Returns a promise that could be rejected.
12
13
  // @TODO review together with Segments and MySegments storage APIs
@@ -21,13 +22,14 @@ function checkAllSegmentsExist(segments) {
21
22
  * Collect segments from a raw split definition.
22
23
  * Exported for testing purposes.
23
24
  */
24
- function parseSegments(_a) {
25
- var conditions = _a.conditions;
26
- var segments = new Set();
25
+ function parseSegments(ruleEntity, matcherType) {
26
+ if (matcherType === void 0) { matcherType = constants_3.IN_SEGMENT; }
27
+ var _a = ruleEntity, _b = _a.conditions, conditions = _b === void 0 ? [] : _b, excluded = _a.excluded;
28
+ var segments = new Set(excluded && excluded.segments);
27
29
  for (var i = 0; i < conditions.length; i++) {
28
30
  var matchers = conditions[i].matcherGroup.matchers;
29
31
  matchers.forEach(function (matcher) {
30
- if (matcher.matcherType === constants_3.IN_SEGMENT)
32
+ if (matcher.matcherType === matcherType)
31
33
  segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
32
34
  });
33
35
  }
@@ -58,24 +60,21 @@ function matchFilters(featureFlag, filters) {
58
60
  * i.e., an object with added splits, removed splits and used segments.
59
61
  * Exported for testing purposes.
60
62
  */
61
- function computeSplitsMutation(entries, filters) {
62
- var segments = new Set();
63
- var computed = entries.reduce(function (accum, split) {
64
- if (split.status === 'ACTIVE' && matchFilters(split, filters)) {
65
- accum.added.push(split);
66
- parseSegments(split).forEach(function (segmentName) {
63
+ function computeMutation(rules, segments, filters) {
64
+ return rules.reduce(function (accum, ruleEntity) {
65
+ if (ruleEntity.status === 'ACTIVE' && (!filters || matchFilters(ruleEntity, filters))) {
66
+ accum.added.push(ruleEntity);
67
+ parseSegments(ruleEntity).forEach(function (segmentName) {
67
68
  segments.add(segmentName);
68
69
  });
69
70
  }
70
71
  else {
71
- accum.removed.push(split);
72
+ accum.removed.push(ruleEntity);
72
73
  }
73
74
  return accum;
74
- }, { added: [], removed: [], segments: [] });
75
- computed.segments = (0, sets_1.setToArray)(segments);
76
- return computed;
75
+ }, { added: [], removed: [] });
77
76
  }
78
- exports.computeSplitsMutation = computeSplitsMutation;
77
+ exports.computeMutation = computeMutation;
79
78
  /**
80
79
  * factory of SplitChanges updater, a task that:
81
80
  * - fetches split changes using `splitChangesFetcher`
@@ -93,7 +92,7 @@ exports.computeSplitsMutation = computeSplitsMutation;
93
92
  function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFiltersValidation, splitsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, isClientSide) {
94
93
  if (requestTimeoutBeforeReady === void 0) { requestTimeoutBeforeReady = 0; }
95
94
  if (retriesOnFailureBeforeReady === void 0) { retriesOnFailureBeforeReady = 0; }
96
- var splits = storage.splits, segments = storage.segments;
95
+ var splits = storage.splits, rbSegments = storage.rbSegments, segments = storage.segments;
97
96
  var startingUp = true;
98
97
  /** timeout decorator for `splitChangesFetcher` promise */
99
98
  function _promiseDecorator(promise) {
@@ -108,29 +107,48 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFilt
108
107
  * @param noCache - true to revalidate data to fetch
109
108
  * @param till - query param to bypass CDN requests
110
109
  */
111
- return function splitChangesUpdater(noCache, till, splitUpdateNotification) {
110
+ return function splitChangesUpdater(noCache, till, instantUpdate) {
112
111
  /**
113
112
  * @param since - current changeNumber at splitsCache
114
113
  * @param retry - current number of retry attempts
115
114
  */
116
- function _splitChangesUpdater(since, retry) {
115
+ function _splitChangesUpdater(sinces, retry) {
117
116
  if (retry === void 0) { retry = 0; }
118
- log.debug(constants_2.SYNC_SPLITS_FETCH, [since]);
119
- return Promise.resolve(splitUpdateNotification ?
120
- { splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
121
- splitChangesFetcher(since, noCache, till, _promiseDecorator))
117
+ var since = sinces[0], rbSince = sinces[1];
118
+ log.debug(constants_2.SYNC_SPLITS_FETCH, sinces);
119
+ return Promise.resolve(instantUpdate ?
120
+ instantUpdate.type === constants_4.SPLIT_UPDATE ?
121
+ // IFFU edge case: a change to a flag that adds an IN_RULE_BASED_SEGMENT matcher that is not present yet
122
+ Promise.resolve(rbSegments.contains(parseSegments(instantUpdate.payload, constants_3.IN_RULE_BASED_SEGMENT))).then(function (contains) {
123
+ return contains ?
124
+ { ff: { d: [instantUpdate.payload], t: instantUpdate.changeNumber } } :
125
+ splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator);
126
+ }) :
127
+ { rbs: { d: [instantUpdate.payload], t: instantUpdate.changeNumber } } :
128
+ splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator))
122
129
  .then(function (splitChanges) {
123
130
  startingUp = false;
124
- var mutation = computeSplitsMutation(splitChanges.splits, splitFiltersValidation);
125
- log.debug(constants_2.SYNC_SPLITS_UPDATE, [mutation.added.length, mutation.removed.length, mutation.segments.length]);
126
- return Promise.all([
127
- splits.update(mutation.added, mutation.removed, splitChanges.till),
128
- segments.registerSegments(mutation.segments)
131
+ var usedSegments = new Set();
132
+ var ffUpdate = false;
133
+ if (splitChanges.ff) {
134
+ var _a = computeMutation(splitChanges.ff.d, usedSegments, splitFiltersValidation), added = _a.added, removed = _a.removed;
135
+ log.debug(constants_2.SYNC_SPLITS_UPDATE, [added.length, removed.length]);
136
+ ffUpdate = splits.update(added, removed, splitChanges.ff.t);
137
+ }
138
+ var rbsUpdate = false;
139
+ if (splitChanges.rbs) {
140
+ var _b = computeMutation(splitChanges.rbs.d, usedSegments), added = _b.added, removed = _b.removed;
141
+ log.debug(constants_2.SYNC_RBS_UPDATE, [added.length, removed.length]);
142
+ rbsUpdate = rbSegments.update(added, removed, splitChanges.rbs.t);
143
+ }
144
+ return Promise.all([ffUpdate, rbsUpdate,
145
+ // @TODO if at least 1 segment fetch fails due to 404 and other segments are updated in the storage, SDK_UPDATE is not emitted
146
+ segments.registerSegments((0, sets_1.setToArray)(usedSegments))
129
147
  ]).then(function (_a) {
130
- var isThereUpdate = _a[0];
148
+ var ffChanged = _a[0], rbsChanged = _a[1];
131
149
  if (splitsEventEmitter) {
132
150
  // To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
133
- return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && isThereUpdate && (isClientSide || checkAllSegmentsExist(segments))))
151
+ return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
134
152
  .catch(function () { return false; } /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
135
153
  .then(function (emitSplitsArrivedEvent) {
136
154
  // emit SDK events
@@ -147,7 +165,7 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFilt
147
165
  if (startingUp && retriesOnFailureBeforeReady > retry) {
148
166
  retry += 1;
149
167
  log.info(constants_2.SYNC_SPLITS_FETCH_RETRY, [retry, error]);
150
- return _splitChangesUpdater(since, retry);
168
+ return _splitChangesUpdater(sinces, retry);
151
169
  }
152
170
  else {
153
171
  startingUp = false;
@@ -155,8 +173,8 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFilt
155
173
  return false;
156
174
  });
157
175
  }
158
- var sincePromise = Promise.resolve(splits.getChangeNumber()); // `getChangeNumber` never rejects or throws error
159
- return sincePromise.then(_splitChangesUpdater);
176
+ // `getChangeNumber` never rejects or throws error
177
+ return Promise.all([splits.getChangeNumber(), rbSegments.getChangeNumber()]).then(_splitChangesUpdater);
160
178
  };
161
179
  }
162
180
  exports.splitChangesUpdaterFactory = splitChangesUpdaterFactory;
@@ -78,6 +78,7 @@ function SSEHandlerFactory(log, pushEmitter, telemetryTracker) {
78
78
  case constants_1.MEMBERSHIPS_MS_UPDATE:
79
79
  case constants_1.MEMBERSHIPS_LS_UPDATE:
80
80
  case constants_1.SPLIT_KILL:
81
+ case constants_1.RB_SEGMENT_UPDATE:
81
82
  pushEmitter.emit(parsedData.type, parsedData);
82
83
  break;
83
84
  /* occupancy & control events, handled by NotificationManagerKeeper */