@splitsoftware/splitio-commons 2.7.0 → 2.7.2-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 +7 -1
- package/cjs/logger/index.js +0 -3
- package/cjs/logger/messages/info.js +1 -1
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +16 -5
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -2
- package/esm/logger/index.js +0 -3
- package/esm/logger/messages/info.js +1 -1
- package/esm/logger/messages/warn.js +1 -1
- package/esm/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +16 -5
- package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -2
- package/package.json +1 -1
- package/src/logger/index.ts +0 -2
- package/src/logger/messages/info.ts +1 -1
- package/src/logger/messages/warn.ts +1 -1
- package/src/sync/polling/syncTasks/segmentsSyncTask.ts +2 -0
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +17 -4
- package/src/sync/polling/updaters/splitChangesUpdater.ts +4 -5
- package/types/splitio.d.ts +1 -4
package/CHANGES.txt
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
2.8.0 (October XX, 2025)
|
|
2
|
+
- Updated the SDK’s initial synchronization in Node.js (server-side) to use `startup.requestTimeoutBeforeReady` and `startup.retriesOnFailureBeforeReady` to control the timeout and retry behavior of segment requests.
|
|
3
|
+
|
|
4
|
+
2.7.1 (October 8, 2025)
|
|
5
|
+
- Bugfix - Update `debug` option to support log levels when `logger` option is used.
|
|
6
|
+
|
|
1
7
|
2.7.0 (October 7, 2025)
|
|
2
|
-
- Added support for custom loggers: added `logger` configuration option and `
|
|
8
|
+
- Added support for custom loggers: added `logger` configuration option and `factory.Logger.setLogger` method to allow the SDK to use a custom logger.
|
|
3
9
|
|
|
4
10
|
2.6.0 (September 18, 2025)
|
|
5
11
|
- Added `storage.wrapper` configuration option to allow the SDK to use a custom storage wrapper for the storage type `LOCALSTORAGE`. Default value is `window.localStorage`.
|
package/cjs/logger/index.js
CHANGED
|
@@ -63,9 +63,6 @@ var Logger = /** @class */ (function () {
|
|
|
63
63
|
if (logger) {
|
|
64
64
|
if ((0, commons_1.isLogger)(logger)) {
|
|
65
65
|
this.logger = logger;
|
|
66
|
-
// If custom logger is set, all logs are either enabled or disabled
|
|
67
|
-
if (this.logLevel !== LogLevelIndexes.NONE)
|
|
68
|
-
this.setLogLevel(exports.LogLevels.DEBUG);
|
|
69
66
|
return;
|
|
70
67
|
}
|
|
71
68
|
else {
|
|
@@ -23,7 +23,7 @@ exports.codesInfo = warn_1.codesWarn.concat([
|
|
|
23
23
|
[c.POLLING_SMART_PAUSING, c.LOG_PREFIX_SYNC_POLLING + 'Turning segments data polling %s.'],
|
|
24
24
|
[c.POLLING_START, c.LOG_PREFIX_SYNC_POLLING + 'Starting polling'],
|
|
25
25
|
[c.POLLING_STOP, c.LOG_PREFIX_SYNC_POLLING + 'Stopping polling'],
|
|
26
|
-
[c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying
|
|
26
|
+
[c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying fetch of feature flags (attempt #%s). Reason: %s'],
|
|
27
27
|
[c.SUBMITTERS_PUSH_FULL_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full %s queue and resetting timer.'],
|
|
28
28
|
[c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s.'],
|
|
29
29
|
[c.SUBMITTERS_PUSH_PAGE_HIDDEN, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing %s because page became hidden.'],
|
|
@@ -9,7 +9,7 @@ exports.codesWarn = error_1.codesError.concat([
|
|
|
9
9
|
[c.ENGINE_VALUE_INVALID, c.LOG_PREFIX_ENGINE_VALUE + 'Value %s doesn\'t match with expected type.'],
|
|
10
10
|
[c.ENGINE_VALUE_NO_ATTRIBUTES, c.LOG_PREFIX_ENGINE_VALUE + 'Defined attribute `%s`. No attributes received.'],
|
|
11
11
|
// synchronizer
|
|
12
|
-
[c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying
|
|
12
|
+
[c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying fetch of memberships (attempt #%s). Reason: %s'],
|
|
13
13
|
[c.SYNC_SPLITS_FETCH_FAILS, c.LOG_PREFIX_SYNC_SPLITS + 'Error while doing fetch of feature flags. %s'],
|
|
14
14
|
[c.STREAMING_PARSING_ERROR_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE error notification: %s'],
|
|
15
15
|
[c.STREAMING_PARSING_MESSAGE_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE message notification: %s'],
|
|
@@ -8,6 +8,6 @@ var segmentChangesUpdater_1 = require("../updaters/segmentChangesUpdater");
|
|
|
8
8
|
* Creates a sync task that periodically executes a `segmentChangesUpdater` task
|
|
9
9
|
*/
|
|
10
10
|
function segmentsSyncTaskFactory(fetchSegmentChanges, storage, readiness, settings) {
|
|
11
|
-
return (0, syncTask_1.syncTaskFactory)(settings.log, (0, segmentChangesUpdater_1.segmentChangesUpdaterFactory)(settings.log, (0, segmentChangesFetcher_1.segmentChangesFetcherFactory)(fetchSegmentChanges), storage.segments, readiness), settings.scheduler.segmentsRefreshRate, 'segmentChangesUpdater');
|
|
11
|
+
return (0, syncTask_1.syncTaskFactory)(settings.log, (0, segmentChangesUpdater_1.segmentChangesUpdaterFactory)(settings.log, (0, segmentChangesFetcher_1.segmentChangesFetcherFactory)(fetchSegmentChanges), storage.segments, readiness, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady), settings.scheduler.segmentsRefreshRate, 'segmentChangesUpdater');
|
|
12
12
|
}
|
|
13
13
|
exports.segmentsSyncTaskFactory = segmentsSyncTaskFactory;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.segmentChangesUpdaterFactory = void 0;
|
|
4
4
|
var constants_1 = require("../../../readiness/constants");
|
|
5
5
|
var constants_2 = require("../../../logger/constants");
|
|
6
|
+
var timeout_1 = require("../../../utils/promise/timeout");
|
|
6
7
|
/**
|
|
7
8
|
* Factory of SegmentChanges updater, a task that:
|
|
8
9
|
* - fetches segment changes using `segmentChangesFetcher`
|
|
@@ -14,22 +15,33 @@ var constants_2 = require("../../../logger/constants");
|
|
|
14
15
|
* @param segments - segments storage, with sync or async methods
|
|
15
16
|
* @param readiness - optional readiness manager. Not required for synchronizer or producer mode.
|
|
16
17
|
*/
|
|
17
|
-
function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, readiness) {
|
|
18
|
+
function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, readiness, requestTimeoutBeforeReady, retriesOnFailureBeforeReady) {
|
|
18
19
|
var readyOnAlreadyExistentState = true;
|
|
19
|
-
function
|
|
20
|
+
function _promiseDecorator(promise) {
|
|
21
|
+
if (readyOnAlreadyExistentState && requestTimeoutBeforeReady)
|
|
22
|
+
promise = (0, timeout_1.timeout)(requestTimeoutBeforeReady, promise);
|
|
23
|
+
return promise;
|
|
24
|
+
}
|
|
25
|
+
function updateSegment(segmentName, noCache, till, fetchOnlyNew, retries) {
|
|
20
26
|
log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Processing segment " + segmentName);
|
|
21
27
|
var sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
|
|
22
28
|
return sincePromise.then(function (since) {
|
|
23
29
|
// if fetchOnlyNew flag, avoid processing already fetched segments
|
|
24
30
|
return fetchOnlyNew && since !== undefined ?
|
|
25
31
|
false :
|
|
26
|
-
segmentChangesFetcher(since || -1, segmentName, noCache, till).then(function (changes) {
|
|
32
|
+
segmentChangesFetcher(since || -1, segmentName, noCache, till, _promiseDecorator).then(function (changes) {
|
|
27
33
|
return Promise.all(changes.map(function (x) {
|
|
28
34
|
log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Processing " + segmentName + " with till = " + x.till + ". Added: " + x.added.length + ". Removed: " + x.removed.length);
|
|
29
35
|
return segments.update(segmentName, x.added, x.removed, x.till);
|
|
30
36
|
})).then(function (updates) {
|
|
31
37
|
return updates.some(function (update) { return update; });
|
|
32
38
|
});
|
|
39
|
+
}).catch(function (error) {
|
|
40
|
+
if (retries) {
|
|
41
|
+
log.warn(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Retrying fetch of segment " + segmentName + " (attempt #" + retries + "). Reason: " + error);
|
|
42
|
+
return updateSegment(segmentName, noCache, till, fetchOnlyNew, retries - 1);
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
33
45
|
});
|
|
34
46
|
});
|
|
35
47
|
}
|
|
@@ -49,8 +61,7 @@ function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, read
|
|
|
49
61
|
// If not a segment name provided, read list of available segments names to be updated.
|
|
50
62
|
var segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments());
|
|
51
63
|
return segmentsPromise.then(function (segmentNames) {
|
|
52
|
-
|
|
53
|
-
var updaters = segmentNames.map(function (segmentName) { return updateSegment(segmentName, noCache, till, fetchOnlyNew); });
|
|
64
|
+
var updaters = segmentNames.map(function (segmentName) { return updateSegment(segmentName, noCache, till, fetchOnlyNew, readyOnAlreadyExistentState ? retriesOnFailureBeforeReady : 0); });
|
|
54
65
|
return Promise.all(updaters).then(function (shouldUpdateFlags) {
|
|
55
66
|
// if at least one segment fetch succeeded, mark segments ready
|
|
56
67
|
if (shouldUpdateFlags.some(function (update) { return update; }) || readyOnAlreadyExistentState) {
|
|
@@ -171,14 +171,14 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFilt
|
|
|
171
171
|
});
|
|
172
172
|
})
|
|
173
173
|
.catch(function (error) {
|
|
174
|
-
log.warn(constants_2.SYNC_SPLITS_FETCH_FAILS, [error]);
|
|
175
174
|
if (startingUp && retriesOnFailureBeforeReady > retry) {
|
|
176
175
|
retry += 1;
|
|
177
|
-
log.
|
|
176
|
+
log.warn(constants_2.SYNC_SPLITS_FETCH_RETRY, [retry, error]);
|
|
178
177
|
return _splitChangesUpdater(sinces, retry);
|
|
179
178
|
}
|
|
180
179
|
else {
|
|
181
180
|
startingUp = false;
|
|
181
|
+
log.warn(constants_2.SYNC_SPLITS_FETCH_FAILS, [error]);
|
|
182
182
|
}
|
|
183
183
|
return false;
|
|
184
184
|
});
|
package/esm/logger/index.js
CHANGED
|
@@ -58,9 +58,6 @@ var Logger = /** @class */ (function () {
|
|
|
58
58
|
if (logger) {
|
|
59
59
|
if (isLogger(logger)) {
|
|
60
60
|
this.logger = logger;
|
|
61
|
-
// If custom logger is set, all logs are either enabled or disabled
|
|
62
|
-
if (this.logLevel !== LogLevelIndexes.NONE)
|
|
63
|
-
this.setLogLevel(LogLevels.DEBUG);
|
|
64
61
|
return;
|
|
65
62
|
}
|
|
66
63
|
else {
|
|
@@ -19,7 +19,7 @@ export var codesInfo = codesWarn.concat([
|
|
|
19
19
|
[c.POLLING_SMART_PAUSING, c.LOG_PREFIX_SYNC_POLLING + 'Turning segments data polling %s.'],
|
|
20
20
|
[c.POLLING_START, c.LOG_PREFIX_SYNC_POLLING + 'Starting polling'],
|
|
21
21
|
[c.POLLING_STOP, c.LOG_PREFIX_SYNC_POLLING + 'Stopping polling'],
|
|
22
|
-
[c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying
|
|
22
|
+
[c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying fetch of feature flags (attempt #%s). Reason: %s'],
|
|
23
23
|
[c.SUBMITTERS_PUSH_FULL_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full %s queue and resetting timer.'],
|
|
24
24
|
[c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s.'],
|
|
25
25
|
[c.SUBMITTERS_PUSH_PAGE_HIDDEN, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing %s because page became hidden.'],
|
|
@@ -5,7 +5,7 @@ export var codesWarn = codesError.concat([
|
|
|
5
5
|
[c.ENGINE_VALUE_INVALID, c.LOG_PREFIX_ENGINE_VALUE + 'Value %s doesn\'t match with expected type.'],
|
|
6
6
|
[c.ENGINE_VALUE_NO_ATTRIBUTES, c.LOG_PREFIX_ENGINE_VALUE + 'Defined attribute `%s`. No attributes received.'],
|
|
7
7
|
// synchronizer
|
|
8
|
-
[c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying
|
|
8
|
+
[c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying fetch of memberships (attempt #%s). Reason: %s'],
|
|
9
9
|
[c.SYNC_SPLITS_FETCH_FAILS, c.LOG_PREFIX_SYNC_SPLITS + 'Error while doing fetch of feature flags. %s'],
|
|
10
10
|
[c.STREAMING_PARSING_ERROR_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE error notification: %s'],
|
|
11
11
|
[c.STREAMING_PARSING_MESSAGE_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE message notification: %s'],
|
|
@@ -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
|
}
|
|
@@ -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) {
|
|
@@ -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
package/src/logger/index.ts
CHANGED
|
@@ -72,8 +72,6 @@ export class Logger implements ILogger {
|
|
|
72
72
|
if (logger) {
|
|
73
73
|
if (isLogger(logger)) {
|
|
74
74
|
this.logger = logger;
|
|
75
|
-
// If custom logger is set, all logs are either enabled or disabled
|
|
76
|
-
if (this.logLevel !== LogLevelIndexes.NONE) this.setLogLevel(LogLevels.DEBUG);
|
|
77
75
|
return;
|
|
78
76
|
} else {
|
|
79
77
|
this.error('Invalid `logger` instance. It must be an object with `debug`, `info`, `warn` and `error` methods. Defaulting to `console.log`');
|
|
@@ -22,7 +22,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([
|
|
|
22
22
|
[c.POLLING_SMART_PAUSING, c.LOG_PREFIX_SYNC_POLLING + 'Turning segments data polling %s.'],
|
|
23
23
|
[c.POLLING_START, c.LOG_PREFIX_SYNC_POLLING + 'Starting polling'],
|
|
24
24
|
[c.POLLING_STOP, c.LOG_PREFIX_SYNC_POLLING + 'Stopping polling'],
|
|
25
|
-
[c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying
|
|
25
|
+
[c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying fetch of feature flags (attempt #%s). Reason: %s'],
|
|
26
26
|
[c.SUBMITTERS_PUSH_FULL_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full %s queue and resetting timer.'],
|
|
27
27
|
[c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s.'],
|
|
28
28
|
[c.SUBMITTERS_PUSH_PAGE_HIDDEN, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing %s because page became hidden.'],
|
|
@@ -6,7 +6,7 @@ export const codesWarn: [number, string][] = codesError.concat([
|
|
|
6
6
|
[c.ENGINE_VALUE_INVALID, c.LOG_PREFIX_ENGINE_VALUE + 'Value %s doesn\'t match with expected type.'],
|
|
7
7
|
[c.ENGINE_VALUE_NO_ATTRIBUTES, c.LOG_PREFIX_ENGINE_VALUE + 'Defined attribute `%s`. No attributes received.'],
|
|
8
8
|
// synchronizer
|
|
9
|
-
[c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying
|
|
9
|
+
[c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying fetch of memberships (attempt #%s). Reason: %s'],
|
|
10
10
|
[c.SYNC_SPLITS_FETCH_FAILS, c.LOG_PREFIX_SYNC_SPLITS + 'Error while doing fetch of feature flags. %s'],
|
|
11
11
|
[c.STREAMING_PARSING_ERROR_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE error notification: %s'],
|
|
12
12
|
[c.STREAMING_PARSING_MESSAGE_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE message notification: %s'],
|
|
@@ -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'
|
|
@@ -4,6 +4,7 @@ import { IReadinessManager } from '../../../readiness/types';
|
|
|
4
4
|
import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
|
|
5
5
|
import { ILogger } from '../../../logger/types';
|
|
6
6
|
import { LOG_PREFIX_INSTANTIATION, LOG_PREFIX_SYNC_SEGMENTS } from '../../../logger/constants';
|
|
7
|
+
import { timeout } from '../../../utils/promise/timeout';
|
|
7
8
|
|
|
8
9
|
type ISegmentChangesUpdater = (fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number) => Promise<boolean>
|
|
9
10
|
|
|
@@ -23,11 +24,18 @@ export function segmentChangesUpdaterFactory(
|
|
|
23
24
|
segmentChangesFetcher: ISegmentChangesFetcher,
|
|
24
25
|
segments: ISegmentsCacheBase,
|
|
25
26
|
readiness?: IReadinessManager,
|
|
27
|
+
requestTimeoutBeforeReady?: number,
|
|
28
|
+
retriesOnFailureBeforeReady?: number,
|
|
26
29
|
): ISegmentChangesUpdater {
|
|
27
30
|
|
|
28
31
|
let readyOnAlreadyExistentState = true;
|
|
29
32
|
|
|
30
|
-
function
|
|
33
|
+
function _promiseDecorator<T>(promise: Promise<T>) {
|
|
34
|
+
if (readyOnAlreadyExistentState && requestTimeoutBeforeReady) promise = timeout(requestTimeoutBeforeReady, promise);
|
|
35
|
+
return promise;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function updateSegment(segmentName: string, noCache?: boolean, till?: number, fetchOnlyNew?: boolean, retries?: number): Promise<boolean> {
|
|
31
39
|
log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing segment ${segmentName}`);
|
|
32
40
|
let sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
|
|
33
41
|
|
|
@@ -35,13 +43,19 @@ export function segmentChangesUpdaterFactory(
|
|
|
35
43
|
// if fetchOnlyNew flag, avoid processing already fetched segments
|
|
36
44
|
return fetchOnlyNew && since !== undefined ?
|
|
37
45
|
false :
|
|
38
|
-
segmentChangesFetcher(since || -1, segmentName, noCache, till).then((changes) => {
|
|
46
|
+
segmentChangesFetcher(since || -1, segmentName, noCache, till, _promiseDecorator).then((changes) => {
|
|
39
47
|
return Promise.all(changes.map(x => {
|
|
40
48
|
log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing ${segmentName} with till = ${x.till}. Added: ${x.added.length}. Removed: ${x.removed.length}`);
|
|
41
49
|
return segments.update(segmentName, x.added, x.removed, x.till);
|
|
42
50
|
})).then((updates) => {
|
|
43
51
|
return updates.some(update => update);
|
|
44
52
|
});
|
|
53
|
+
}).catch(error => {
|
|
54
|
+
if (retries) {
|
|
55
|
+
log.warn(`${LOG_PREFIX_SYNC_SEGMENTS}Retrying fetch of segment ${segmentName} (attempt #${retries}). Reason: ${error}`);
|
|
56
|
+
return updateSegment(segmentName, noCache, till, fetchOnlyNew, retries - 1);
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
45
59
|
});
|
|
46
60
|
});
|
|
47
61
|
}
|
|
@@ -63,8 +77,7 @@ export function segmentChangesUpdaterFactory(
|
|
|
63
77
|
let segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments());
|
|
64
78
|
|
|
65
79
|
return segmentsPromise.then(segmentNames => {
|
|
66
|
-
|
|
67
|
-
const updaters = segmentNames.map(segmentName => updateSegment(segmentName, noCache, till, fetchOnlyNew));
|
|
80
|
+
const updaters = segmentNames.map(segmentName => updateSegment(segmentName, noCache, till, fetchOnlyNew, readyOnAlreadyExistentState ? retriesOnFailureBeforeReady : 0));
|
|
68
81
|
|
|
69
82
|
return Promise.all(updaters).then(shouldUpdateFlags => {
|
|
70
83
|
// if at least one segment fetch succeeded, mark segments ready
|
|
@@ -120,8 +120,8 @@ export function splitChangesUpdaterFactory(
|
|
|
120
120
|
storage: Pick<IStorageBase, 'splits' | 'rbSegments' | 'segments' | 'save'>,
|
|
121
121
|
splitFiltersValidation: ISplitFiltersValidation,
|
|
122
122
|
splitsEventEmitter?: ISplitsEventEmitter,
|
|
123
|
-
requestTimeoutBeforeReady
|
|
124
|
-
retriesOnFailureBeforeReady
|
|
123
|
+
requestTimeoutBeforeReady = 0,
|
|
124
|
+
retriesOnFailureBeforeReady = 0,
|
|
125
125
|
isClientSide?: boolean
|
|
126
126
|
): SplitChangesUpdater {
|
|
127
127
|
const { splits, rbSegments, segments } = storage;
|
|
@@ -201,14 +201,13 @@ export function splitChangesUpdaterFactory(
|
|
|
201
201
|
});
|
|
202
202
|
})
|
|
203
203
|
.catch(error => {
|
|
204
|
-
log.warn(SYNC_SPLITS_FETCH_FAILS, [error]);
|
|
205
|
-
|
|
206
204
|
if (startingUp && retriesOnFailureBeforeReady > retry) {
|
|
207
205
|
retry += 1;
|
|
208
|
-
log.
|
|
206
|
+
log.warn(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
|
|
209
207
|
return _splitChangesUpdater(sinces, retry);
|
|
210
208
|
} else {
|
|
211
209
|
startingUp = false;
|
|
210
|
+
log.warn(SYNC_SPLITS_FETCH_FAILS, [error]);
|
|
212
211
|
}
|
|
213
212
|
return false;
|
|
214
213
|
});
|
package/types/splitio.d.ts
CHANGED
|
@@ -93,6 +93,7 @@ interface ISharedSettings {
|
|
|
93
93
|
urls?: SplitIO.UrlSettings;
|
|
94
94
|
/**
|
|
95
95
|
* Custom logger object. If not provided, the SDK will use the default `console.log` method for all log levels.
|
|
96
|
+
* Set together with `debug` option to `true` or a log level string to enable logging.
|
|
96
97
|
*/
|
|
97
98
|
logger?: SplitIO.Logger;
|
|
98
99
|
}
|
|
@@ -145,8 +146,6 @@ interface IPluggableSharedSettings {
|
|
|
145
146
|
* config.debug = ErrorLogger()
|
|
146
147
|
* ```
|
|
147
148
|
*
|
|
148
|
-
* When combined with the `logger` option, any log level other than `NONE` (false) will be set to `DEBUG` (true), delegating log level control to the custom logger.
|
|
149
|
-
*
|
|
150
149
|
* @defaultValue `false`
|
|
151
150
|
*/
|
|
152
151
|
debug?: boolean | SplitIO.LogLevel | SplitIO.ILogger;
|
|
@@ -170,8 +169,6 @@ interface INonPluggableSharedSettings {
|
|
|
170
169
|
* config.debug = 'WARN'
|
|
171
170
|
* ```
|
|
172
171
|
*
|
|
173
|
-
* When combined with the `logger` option, any log level other than `NONE` (false) will be set to `DEBUG` (true), delegating log level control to the custom logger.
|
|
174
|
-
*
|
|
175
172
|
* @defaultValue `false`
|
|
176
173
|
*/
|
|
177
174
|
debug?: boolean | SplitIO.LogLevel;
|