@splitsoftware/splitio-commons 2.8.0 → 2.8.1-rc.0
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 +4 -0
- package/cjs/storages/AbstractMySegmentsCacheSync.js +31 -23
- package/cjs/storages/AbstractSplitsCacheSync.js +3 -2
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +10 -28
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +22 -33
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +19 -29
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +3 -2
- package/cjs/storages/inRedis/SegmentsCacheInRedis.js +1 -1
- package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +16 -5
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +3 -3
- package/esm/storages/AbstractMySegmentsCacheSync.js +31 -23
- package/esm/storages/AbstractSplitsCacheSync.js +3 -2
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +11 -29
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +22 -33
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +19 -29
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +3 -2
- package/esm/storages/inRedis/SegmentsCacheInRedis.js +1 -1
- package/esm/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +16 -5
- package/esm/sync/polling/updaters/splitChangesUpdater.js +3 -3
- package/package.json +1 -1
- package/src/storages/AbstractMySegmentsCacheSync.ts +26 -20
- package/src/storages/AbstractSplitsCacheSync.ts +3 -2
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +9 -24
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +18 -27
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +22 -29
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +3 -2
- package/src/storages/inRedis/SegmentsCacheInRedis.ts +1 -1
- package/src/sync/polling/syncTasks/segmentsSyncTask.ts +2 -0
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +3 -3
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +17 -4
- package/src/sync/polling/updaters/splitChangesUpdater.ts +6 -7
- package/types/splitio.d.ts +0 -2
|
@@ -15,9 +15,10 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
15
15
|
};
|
|
16
16
|
RBSegmentsCacheInLocal.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
17
17
|
var _this = this;
|
|
18
|
-
this.setChangeNumber(changeNumber);
|
|
19
18
|
var updated = toAdd.map(function (toAdd) { return _this.add(toAdd); }).some(function (result) { return result; });
|
|
20
|
-
|
|
19
|
+
updated = toRemove.map(function (toRemove) { return _this.remove(toRemove.name); }).some(function (result) { return result; }) || updated;
|
|
20
|
+
this.setChangeNumber(changeNumber);
|
|
21
|
+
return updated;
|
|
21
22
|
};
|
|
22
23
|
RBSegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
23
24
|
try {
|
|
@@ -37,40 +38,28 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
37
38
|
this.storage.removeItem(segmentsCountKey);
|
|
38
39
|
};
|
|
39
40
|
RBSegmentsCacheInLocal.prototype.add = function (rbSegment) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
catch (e) {
|
|
56
|
-
this.log.error(LOG_PREFIX + e);
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
41
|
+
var name = rbSegment.name;
|
|
42
|
+
var rbSegmentKey = this.keys.buildRBSegmentKey(name);
|
|
43
|
+
var rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
|
|
44
|
+
var previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
|
|
45
|
+
this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
|
|
46
|
+
var usesSegmentsDiff = 0;
|
|
47
|
+
if (previous && usesSegments(previous))
|
|
48
|
+
usesSegmentsDiff--;
|
|
49
|
+
if (usesSegments(rbSegment))
|
|
50
|
+
usesSegmentsDiff++;
|
|
51
|
+
if (usesSegmentsDiff !== 0)
|
|
52
|
+
this.updateSegmentCount(usesSegmentsDiff);
|
|
53
|
+
return true;
|
|
59
54
|
};
|
|
60
55
|
RBSegmentsCacheInLocal.prototype.remove = function (name) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (!rbSegment)
|
|
64
|
-
return false;
|
|
65
|
-
this.storage.removeItem(this.keys.buildRBSegmentKey(name));
|
|
66
|
-
if (usesSegments(rbSegment))
|
|
67
|
-
this.updateSegmentCount(-1);
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
catch (e) {
|
|
71
|
-
this.log.error(LOG_PREFIX + e);
|
|
56
|
+
var rbSegment = this.get(name);
|
|
57
|
+
if (!rbSegment)
|
|
72
58
|
return false;
|
|
73
|
-
|
|
59
|
+
this.storage.removeItem(this.keys.buildRBSegmentKey(name));
|
|
60
|
+
if (usesSegments(rbSegment))
|
|
61
|
+
this.updateSegmentCount(-1);
|
|
62
|
+
return true;
|
|
74
63
|
};
|
|
75
64
|
RBSegmentsCacheInLocal.prototype.getNames = function () {
|
|
76
65
|
var len = this.storage.length;
|
|
@@ -67,39 +67,27 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
67
67
|
this.hasSync = false;
|
|
68
68
|
};
|
|
69
69
|
SplitsCacheInLocal.prototype.addSplit = function (split) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
|
|
78
|
-
}
|
|
79
|
-
this.storage.setItem(splitKey, JSON.stringify(split));
|
|
80
|
-
this._incrementCounts(split);
|
|
81
|
-
this.addToFlagSets(split);
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
catch (e) {
|
|
85
|
-
this.log.error(LOG_PREFIX + e);
|
|
86
|
-
return false;
|
|
70
|
+
var name = split.name;
|
|
71
|
+
var splitKey = this.keys.buildSplitKey(name);
|
|
72
|
+
var splitFromStorage = this.storage.getItem(splitKey);
|
|
73
|
+
var previousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : null;
|
|
74
|
+
if (previousSplit) {
|
|
75
|
+
this._decrementCounts(previousSplit);
|
|
76
|
+
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
|
|
87
77
|
}
|
|
78
|
+
this.storage.setItem(splitKey, JSON.stringify(split));
|
|
79
|
+
this._incrementCounts(split);
|
|
80
|
+
this.addToFlagSets(split);
|
|
81
|
+
return true;
|
|
88
82
|
};
|
|
89
83
|
SplitsCacheInLocal.prototype.removeSplit = function (name) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!split)
|
|
93
|
-
return false;
|
|
94
|
-
this.storage.removeItem(this.keys.buildSplitKey(name));
|
|
95
|
-
this._decrementCounts(split);
|
|
96
|
-
this.removeFromFlagSets(split.name, split.sets);
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
catch (e) {
|
|
100
|
-
this.log.error(LOG_PREFIX + e);
|
|
84
|
+
var split = this.getSplit(name);
|
|
85
|
+
if (!split)
|
|
101
86
|
return false;
|
|
102
|
-
|
|
87
|
+
this.storage.removeItem(this.keys.buildSplitKey(name));
|
|
88
|
+
this._decrementCounts(split);
|
|
89
|
+
this.removeFromFlagSets(split.name, split.sets);
|
|
90
|
+
return true;
|
|
103
91
|
};
|
|
104
92
|
SplitsCacheInLocal.prototype.getSplit = function (name) {
|
|
105
93
|
var item = this.storage.getItem(this.keys.buildSplitKey(name));
|
|
@@ -171,6 +159,8 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
171
159
|
var flagSetKey = _this.keys.buildFlagSetKey(featureFlagSet);
|
|
172
160
|
var flagSetFromStorage = _this.storage.getItem(flagSetKey);
|
|
173
161
|
var flagSetCache = new Set(flagSetFromStorage ? JSON.parse(flagSetFromStorage) : []);
|
|
162
|
+
if (flagSetCache.has(featureFlag.name))
|
|
163
|
+
return;
|
|
174
164
|
flagSetCache.add(featureFlag.name);
|
|
175
165
|
_this.storage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
|
|
176
166
|
});
|
|
@@ -13,9 +13,10 @@ var RBSegmentsCacheInMemory = /** @class */ (function () {
|
|
|
13
13
|
};
|
|
14
14
|
RBSegmentsCacheInMemory.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
15
15
|
var _this = this;
|
|
16
|
-
this.changeNumber = changeNumber;
|
|
17
16
|
var updated = toAdd.map(function (toAdd) { return _this.add(toAdd); }).some(function (result) { return result; });
|
|
18
|
-
|
|
17
|
+
updated = toRemove.map(function (toRemove) { return _this.remove(toRemove.name); }).some(function (result) { return result; }) || updated;
|
|
18
|
+
this.changeNumber = changeNumber;
|
|
19
|
+
return updated;
|
|
19
20
|
};
|
|
20
21
|
RBSegmentsCacheInMemory.prototype.add = function (rbSegment) {
|
|
21
22
|
var name = rbSegment.name;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isNaNNumber } from '../../utils/lang';
|
|
2
|
-
import { LOG_PREFIX } from '
|
|
2
|
+
import { LOG_PREFIX } from './constants';
|
|
3
3
|
var SegmentsCacheInRedis = /** @class */ (function () {
|
|
4
4
|
function SegmentsCacheInRedis(log, keys, redis) {
|
|
5
5
|
this.log = log;
|
|
@@ -5,5 +5,5 @@ import { segmentChangesUpdaterFactory } from '../updaters/segmentChangesUpdater'
|
|
|
5
5
|
* Creates a sync task that periodically executes a `segmentChangesUpdater` task
|
|
6
6
|
*/
|
|
7
7
|
export function segmentsSyncTaskFactory(fetchSegmentChanges, storage, readiness, settings) {
|
|
8
|
-
return syncTaskFactory(settings.log, segmentChangesUpdaterFactory(settings.log, segmentChangesFetcherFactory(fetchSegmentChanges), storage.segments, readiness), settings.scheduler.segmentsRefreshRate, 'segmentChangesUpdater');
|
|
8
|
+
return syncTaskFactory(settings.log, segmentChangesUpdaterFactory(settings.log, segmentChangesFetcherFactory(fetchSegmentChanges), storage.segments, readiness, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady), settings.scheduler.segmentsRefreshRate, 'segmentChangesUpdater');
|
|
9
9
|
}
|
|
@@ -45,9 +45,9 @@ export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmen
|
|
|
45
45
|
new Promise(function (res) { updateSegments(segmentsData); res(true); }) :
|
|
46
46
|
// If not provided, fetch mySegments
|
|
47
47
|
mySegmentsFetcher(matchingKey, noCache, till, _promiseDecorator).then(function (segments) {
|
|
48
|
-
// Only when we have downloaded segments completely, we should not keep retrying anymore
|
|
49
|
-
startingUp = false;
|
|
50
48
|
updateSegments(segments);
|
|
49
|
+
// Only when we have downloaded and stored segments completely, we should not keep retrying anymore
|
|
50
|
+
startingUp = false;
|
|
51
51
|
return true;
|
|
52
52
|
});
|
|
53
53
|
return updaterPromise.catch(function (error) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
|
|
2
2
|
import { LOG_PREFIX_INSTANTIATION, LOG_PREFIX_SYNC_SEGMENTS } from '../../../logger/constants';
|
|
3
|
+
import { timeout } from '../../../utils/promise/timeout';
|
|
3
4
|
/**
|
|
4
5
|
* Factory of SegmentChanges updater, a task that:
|
|
5
6
|
* - fetches segment changes using `segmentChangesFetcher`
|
|
@@ -11,22 +12,33 @@ import { LOG_PREFIX_INSTANTIATION, LOG_PREFIX_SYNC_SEGMENTS } from '../../../log
|
|
|
11
12
|
* @param segments - segments storage, with sync or async methods
|
|
12
13
|
* @param readiness - optional readiness manager. Not required for synchronizer or producer mode.
|
|
13
14
|
*/
|
|
14
|
-
export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, readiness) {
|
|
15
|
+
export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, readiness, requestTimeoutBeforeReady, retriesOnFailureBeforeReady) {
|
|
15
16
|
var readyOnAlreadyExistentState = true;
|
|
16
|
-
function
|
|
17
|
+
function _promiseDecorator(promise) {
|
|
18
|
+
if (readyOnAlreadyExistentState && requestTimeoutBeforeReady)
|
|
19
|
+
promise = timeout(requestTimeoutBeforeReady, promise);
|
|
20
|
+
return promise;
|
|
21
|
+
}
|
|
22
|
+
function updateSegment(segmentName, noCache, till, fetchOnlyNew, retries) {
|
|
17
23
|
log.debug(LOG_PREFIX_SYNC_SEGMENTS + "Processing segment " + segmentName);
|
|
18
24
|
var sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
|
|
19
25
|
return sincePromise.then(function (since) {
|
|
20
26
|
// if fetchOnlyNew flag, avoid processing already fetched segments
|
|
21
27
|
return fetchOnlyNew && since !== undefined ?
|
|
22
28
|
false :
|
|
23
|
-
segmentChangesFetcher(since || -1, segmentName, noCache, till).then(function (changes) {
|
|
29
|
+
segmentChangesFetcher(since || -1, segmentName, noCache, till, _promiseDecorator).then(function (changes) {
|
|
24
30
|
return Promise.all(changes.map(function (x) {
|
|
25
31
|
log.debug(LOG_PREFIX_SYNC_SEGMENTS + "Processing " + segmentName + " with till = " + x.till + ". Added: " + x.added.length + ". Removed: " + x.removed.length);
|
|
26
32
|
return segments.update(segmentName, x.added, x.removed, x.till);
|
|
27
33
|
})).then(function (updates) {
|
|
28
34
|
return updates.some(function (update) { return update; });
|
|
29
35
|
});
|
|
36
|
+
}).catch(function (error) {
|
|
37
|
+
if (retries) {
|
|
38
|
+
log.warn(LOG_PREFIX_SYNC_SEGMENTS + "Retrying fetch of segment " + segmentName + " (attempt #" + retries + "). Reason: " + error);
|
|
39
|
+
return updateSegment(segmentName, noCache, till, fetchOnlyNew, retries - 1);
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
30
42
|
});
|
|
31
43
|
});
|
|
32
44
|
}
|
|
@@ -46,8 +58,7 @@ export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segment
|
|
|
46
58
|
// If not a segment name provided, read list of available segments names to be updated.
|
|
47
59
|
var segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments());
|
|
48
60
|
return segmentsPromise.then(function (segmentNames) {
|
|
49
|
-
|
|
50
|
-
var updaters = segmentNames.map(function (segmentName) { return updateSegment(segmentName, noCache, till, fetchOnlyNew); });
|
|
61
|
+
var updaters = segmentNames.map(function (segmentName) { return updateSegment(segmentName, noCache, till, fetchOnlyNew, readyOnAlreadyExistentState ? retriesOnFailureBeforeReady : 0); });
|
|
51
62
|
return Promise.all(updaters).then(function (shouldUpdateFlags) {
|
|
52
63
|
// if at least one segment fetch succeeded, mark segments ready
|
|
53
64
|
if (shouldUpdateFlags.some(function (update) { return update; }) || readyOnAlreadyExistentState) {
|
|
@@ -130,7 +130,6 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, sp
|
|
|
130
130
|
{ rbs: { d: [instantUpdate.payload], t: instantUpdate.changeNumber } } :
|
|
131
131
|
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator))
|
|
132
132
|
.then(function (splitChanges) {
|
|
133
|
-
startingUp = false;
|
|
134
133
|
var usedSegments = new Set();
|
|
135
134
|
var ffUpdate = false;
|
|
136
135
|
if (splitChanges.ff) {
|
|
@@ -151,6 +150,7 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, sp
|
|
|
151
150
|
var ffChanged = _a[0], rbsChanged = _a[1];
|
|
152
151
|
if (storage.save)
|
|
153
152
|
storage.save();
|
|
153
|
+
startingUp = false;
|
|
154
154
|
if (splitsEventEmitter) {
|
|
155
155
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
156
156
|
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
@@ -166,14 +166,14 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, sp
|
|
|
166
166
|
});
|
|
167
167
|
})
|
|
168
168
|
.catch(function (error) {
|
|
169
|
-
log.warn(SYNC_SPLITS_FETCH_FAILS, [error]);
|
|
170
169
|
if (startingUp && retriesOnFailureBeforeReady > retry) {
|
|
171
170
|
retry += 1;
|
|
172
|
-
log.
|
|
171
|
+
log.warn(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
|
|
173
172
|
return _splitChangesUpdater(sinces, retry);
|
|
174
173
|
}
|
|
175
174
|
else {
|
|
176
175
|
startingUp = false;
|
|
176
|
+
log.warn(SYNC_SPLITS_FETCH_FAILS, [error]);
|
|
177
177
|
}
|
|
178
178
|
return false;
|
|
179
179
|
});
|
package/package.json
CHANGED
|
@@ -49,12 +49,10 @@ export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync
|
|
|
49
49
|
* For client-side synchronizer: it resets or updates the cache.
|
|
50
50
|
*/
|
|
51
51
|
resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean {
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
let isDiff = false;
|
|
54
53
|
const { added, removed } = segmentsData as MySegmentsData;
|
|
55
54
|
|
|
56
55
|
if (added && removed) {
|
|
57
|
-
let isDiff = false;
|
|
58
56
|
|
|
59
57
|
added.forEach(segment => {
|
|
60
58
|
isDiff = this.addSegment(segment) || isDiff;
|
|
@@ -63,32 +61,40 @@ export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync
|
|
|
63
61
|
removed.forEach(segment => {
|
|
64
62
|
isDiff = this.removeSegment(segment) || isDiff;
|
|
65
63
|
});
|
|
64
|
+
} else {
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
const names = ((segmentsData as IMySegmentsResponse).k || []).map(s => s.n).sort();
|
|
67
|
+
const storedSegmentKeys = this.getRegisteredSegments().sort();
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
// Extreme fast => everything is empty
|
|
70
|
+
if (!names.length && !storedSegmentKeys.length) {
|
|
71
|
+
isDiff = false;
|
|
72
|
+
} else {
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
if (!names.length && !storedSegmentKeys.length) return false;
|
|
74
|
+
let index = 0;
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index]) index++;
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
// Quick path => no changes
|
|
79
|
+
if (index === names.length && index === storedSegmentKeys.length) {
|
|
80
|
+
isDiff = false;
|
|
81
|
+
} else {
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
// Slowest path => add and/or remove segments
|
|
84
|
+
for (let removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
|
|
85
|
+
this.removeSegment(storedSegmentKeys[removeIndex]);
|
|
86
|
+
}
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
88
|
+
for (let addIndex = index; addIndex < names.length; addIndex++) {
|
|
89
|
+
this.addSegment(names[addIndex]);
|
|
90
|
+
}
|
|
87
91
|
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
isDiff = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
|
|
97
|
+
this.setChangeNumber(segmentsData.cn);
|
|
98
|
+
return isDiff;
|
|
93
99
|
}
|
|
94
100
|
}
|
|
@@ -14,9 +14,10 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
|
|
|
14
14
|
protected abstract setChangeNumber(changeNumber: number): boolean | void
|
|
15
15
|
|
|
16
16
|
update(toAdd: ISplit[], toRemove: ISplit[], changeNumber: number): boolean {
|
|
17
|
+
let updated = toAdd.map(addedFF => this.addSplit(addedFF)).some(result => result);
|
|
18
|
+
updated = toRemove.map(removedFF => this.removeSplit(removedFF.name)).some(result => result) || updated;
|
|
17
19
|
this.setChangeNumber(changeNumber);
|
|
18
|
-
|
|
19
|
-
return toRemove.map(removedFF => this.removeSplit(removedFF.name)).some(result => result) || updated;
|
|
20
|
+
return updated;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
abstract getSplit(name: string): ISplit | null
|
|
@@ -2,7 +2,7 @@ import { ILogger } from '../../logger/types';
|
|
|
2
2
|
import { isNaNNumber } from '../../utils/lang';
|
|
3
3
|
import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
|
|
4
4
|
import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
|
|
5
|
-
import {
|
|
5
|
+
import { DEFINED } from './constants';
|
|
6
6
|
import { StorageAdapter } from '../types';
|
|
7
7
|
|
|
8
8
|
export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
@@ -16,33 +16,22 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
16
16
|
this.log = log;
|
|
17
17
|
this.keys = keys;
|
|
18
18
|
this.storage = storage;
|
|
19
|
-
// There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
protected addSegment(name: string): boolean {
|
|
23
22
|
const segmentKey = this.keys.buildSegmentNameKey(name);
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return true;
|
|
29
|
-
} catch (e) {
|
|
30
|
-
this.log.error(LOG_PREFIX + e);
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
24
|
+
if (this.storage.getItem(segmentKey) === DEFINED) return false;
|
|
25
|
+
this.storage.setItem(segmentKey, DEFINED);
|
|
26
|
+
return true;
|
|
33
27
|
}
|
|
34
28
|
|
|
35
29
|
protected removeSegment(name: string): boolean {
|
|
36
30
|
const segmentKey = this.keys.buildSegmentNameKey(name);
|
|
37
31
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return true;
|
|
42
|
-
} catch (e) {
|
|
43
|
-
this.log.error(LOG_PREFIX + e);
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
32
|
+
if (this.storage.getItem(segmentKey) !== DEFINED) return false;
|
|
33
|
+
this.storage.removeItem(segmentKey);
|
|
34
|
+
return true;
|
|
46
35
|
}
|
|
47
36
|
|
|
48
37
|
isInSegment(name: string): boolean {
|
|
@@ -63,12 +52,8 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
63
52
|
}
|
|
64
53
|
|
|
65
54
|
protected setChangeNumber(changeNumber?: number) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
else this.storage.removeItem(this.keys.buildTillKey());
|
|
69
|
-
} catch (e) {
|
|
70
|
-
this.log.error(e);
|
|
71
|
-
}
|
|
55
|
+
if (changeNumber) this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
|
|
56
|
+
else this.storage.removeItem(this.keys.buildTillKey());
|
|
72
57
|
}
|
|
73
58
|
|
|
74
59
|
getChangeNumber() {
|
|
@@ -26,9 +26,10 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
|
|
29
|
+
let updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
|
|
30
|
+
updated = toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
|
|
29
31
|
this.setChangeNumber(changeNumber);
|
|
30
|
-
|
|
31
|
-
return toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
|
|
32
|
+
return updated;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
private setChangeNumber(changeNumber: number) {
|
|
@@ -48,40 +49,30 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
private add(rbSegment: IRBSegment): boolean {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
|
|
52
|
+
const name = rbSegment.name;
|
|
53
|
+
const rbSegmentKey = this.keys.buildRBSegmentKey(name);
|
|
54
|
+
const rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
|
|
55
|
+
const previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
let usesSegmentsDiff = 0;
|
|
60
|
+
if (previous && usesSegments(previous)) usesSegmentsDiff--;
|
|
61
|
+
if (usesSegments(rbSegment)) usesSegmentsDiff++;
|
|
62
|
+
if (usesSegmentsDiff !== 0) this.updateSegmentCount(usesSegmentsDiff);
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
} catch (e) {
|
|
66
|
-
this.log.error(LOG_PREFIX + e);
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
64
|
+
return true;
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
private remove(name: string): boolean {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (!rbSegment) return false;
|
|
68
|
+
const rbSegment = this.get(name);
|
|
69
|
+
if (!rbSegment) return false;
|
|
75
70
|
|
|
76
|
-
|
|
71
|
+
this.storage.removeItem(this.keys.buildRBSegmentKey(name));
|
|
77
72
|
|
|
78
|
-
|
|
73
|
+
if (usesSegments(rbSegment)) this.updateSegmentCount(-1);
|
|
79
74
|
|
|
80
|
-
|
|
81
|
-
} catch (e) {
|
|
82
|
-
this.log.error(LOG_PREFIX + e);
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
75
|
+
return true;
|
|
85
76
|
}
|
|
86
77
|
|
|
87
78
|
private getNames(): string[] {
|
|
@@ -80,44 +80,34 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
addSplit(split: ISplit) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
83
|
+
const name = split.name;
|
|
84
|
+
const splitKey = this.keys.buildSplitKey(name);
|
|
85
|
+
const splitFromStorage = this.storage.getItem(splitKey);
|
|
86
|
+
const previousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : null;
|
|
87
|
+
|
|
88
|
+
if (previousSplit) {
|
|
89
|
+
this._decrementCounts(previousSplit);
|
|
90
|
+
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
|
|
91
|
+
}
|
|
93
92
|
|
|
94
|
-
|
|
93
|
+
this.storage.setItem(splitKey, JSON.stringify(split));
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
this._incrementCounts(split);
|
|
96
|
+
this.addToFlagSets(split);
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
} catch (e) {
|
|
101
|
-
this.log.error(LOG_PREFIX + e);
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
98
|
+
return true;
|
|
104
99
|
}
|
|
105
100
|
|
|
106
101
|
removeSplit(name: string): boolean {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (!split) return false;
|
|
102
|
+
const split = this.getSplit(name);
|
|
103
|
+
if (!split) return false;
|
|
110
104
|
|
|
111
|
-
|
|
105
|
+
this.storage.removeItem(this.keys.buildSplitKey(name));
|
|
112
106
|
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
this._decrementCounts(split);
|
|
108
|
+
this.removeFromFlagSets(split.name, split.sets);
|
|
115
109
|
|
|
116
|
-
|
|
117
|
-
} catch (e) {
|
|
118
|
-
this.log.error(LOG_PREFIX + e);
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
110
|
+
return true;
|
|
121
111
|
}
|
|
122
112
|
|
|
123
113
|
getSplit(name: string): ISplit | null {
|
|
@@ -206,6 +196,9 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
206
196
|
const flagSetFromStorage = this.storage.getItem(flagSetKey);
|
|
207
197
|
|
|
208
198
|
const flagSetCache = new Set(flagSetFromStorage ? JSON.parse(flagSetFromStorage) : []);
|
|
199
|
+
|
|
200
|
+
if (flagSetCache.has(featureFlag.name)) return;
|
|
201
|
+
|
|
209
202
|
flagSetCache.add(featureFlag.name);
|
|
210
203
|
|
|
211
204
|
this.storage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
|
|
@@ -16,9 +16,10 @@ export class RBSegmentsCacheInMemory implements IRBSegmentsCacheSync {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
|
|
19
|
+
let updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
|
|
20
|
+
updated = toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
|
|
19
21
|
this.changeNumber = changeNumber;
|
|
20
|
-
|
|
21
|
-
return toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
|
|
22
|
+
return updated;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
private add(rbSegment: IRBSegment): boolean {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ILogger } from '../../logger/types';
|
|
2
2
|
import { isNaNNumber } from '../../utils/lang';
|
|
3
|
-
import { LOG_PREFIX } from '
|
|
3
|
+
import { LOG_PREFIX } from './constants';
|
|
4
4
|
import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
5
5
|
import { ISegmentsCacheAsync } from '../types';
|
|
6
6
|
import type { RedisAdapter } from './RedisAdapter';
|
|
@@ -23,6 +23,8 @@ export function segmentsSyncTaskFactory(
|
|
|
23
23
|
segmentChangesFetcherFactory(fetchSegmentChanges),
|
|
24
24
|
storage.segments,
|
|
25
25
|
readiness,
|
|
26
|
+
settings.startup.requestTimeoutBeforeReady,
|
|
27
|
+
settings.startup.retriesOnFailureBeforeReady,
|
|
26
28
|
),
|
|
27
29
|
settings.scheduler.segmentsRefreshRate,
|
|
28
30
|
'segmentChangesUpdater'
|
|
@@ -66,10 +66,10 @@ export function mySegmentsUpdaterFactory(
|
|
|
66
66
|
new Promise((res) => { updateSegments(segmentsData); res(true); }) :
|
|
67
67
|
// If not provided, fetch mySegments
|
|
68
68
|
mySegmentsFetcher(matchingKey, noCache, till, _promiseDecorator).then(segments => {
|
|
69
|
-
// Only when we have downloaded segments completely, we should not keep retrying anymore
|
|
70
|
-
startingUp = false;
|
|
71
|
-
|
|
72
69
|
updateSegments(segments);
|
|
70
|
+
|
|
71
|
+
// Only when we have downloaded and stored segments completely, we should not keep retrying anymore
|
|
72
|
+
startingUp = false;
|
|
73
73
|
return true;
|
|
74
74
|
});
|
|
75
75
|
|