@splitsoftware/splitio-commons 1.3.2-rc.1 → 1.3.2-rc.4
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/cjs/listeners/browser.js +6 -1
- package/cjs/readiness/sdkReadinessManager.js +7 -7
- package/cjs/services/splitApi.js +2 -2
- package/cjs/storages/KeyBuilderSS.js +9 -5
- package/cjs/storages/inRedis/RedisAdapter.js +1 -1
- package/cjs/storages/inRedis/TelemetryCacheInRedis.js +7 -0
- package/cjs/storages/inRedis/index.js +4 -1
- package/cjs/storages/pluggable/index.js +2 -1
- package/cjs/sync/submitters/submitter.js +5 -4
- package/cjs/sync/submitters/telemetrySubmitter.js +22 -9
- package/cjs/utils/settingsValidation/index.js +16 -6
- package/cjs/utils/settingsValidation/url.js +1 -1
- package/esm/listeners/browser.js +6 -1
- package/esm/readiness/sdkReadinessManager.js +7 -7
- package/esm/services/splitApi.js +2 -2
- package/esm/storages/KeyBuilderSS.js +9 -5
- package/esm/storages/inRedis/RedisAdapter.js +1 -1
- package/esm/storages/inRedis/TelemetryCacheInRedis.js +7 -0
- package/esm/storages/inRedis/index.js +4 -1
- package/esm/storages/pluggable/index.js +2 -1
- package/esm/sync/submitters/submitter.js +5 -4
- package/esm/sync/submitters/telemetrySubmitter.js +21 -9
- package/esm/utils/settingsValidation/index.js +17 -7
- package/esm/utils/settingsValidation/url.js +1 -1
- package/package.json +1 -1
- package/src/listeners/browser.ts +6 -1
- package/src/readiness/sdkReadinessManager.ts +7 -5
- package/src/readiness/types.ts +7 -1
- package/src/services/splitApi.ts +2 -2
- package/src/storages/KeyBuilderSS.ts +12 -6
- package/src/storages/inRedis/RedisAdapter.ts +1 -1
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +7 -0
- package/src/storages/inRedis/index.ts +5 -1
- package/src/storages/pluggable/index.ts +2 -1
- package/src/sync/submitters/submitter.ts +4 -4
- package/src/sync/submitters/telemetrySubmitter.ts +24 -10
- package/src/sync/submitters/types.ts +11 -6
- package/src/types.ts +1 -1
- package/src/utils/murmur3/utfx.ts +1 -2
- package/src/utils/settingsValidation/index.ts +18 -8
- package/src/utils/settingsValidation/url.ts +1 -1
- package/types/readiness/sdkReadinessManager.d.ts +1 -3
- package/types/readiness/types.d.ts +6 -1
- package/types/storages/KeyBuilderSS.d.ts +3 -2
- package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +1 -0
- package/types/sync/submitters/telemetrySubmitter.d.ts +3 -2
- package/types/sync/submitters/types.d.ts +8 -5
- package/types/types.d.ts +1 -1
package/cjs/listeners/browser.js
CHANGED
|
@@ -7,6 +7,7 @@ var constants_1 = require("../utils/constants");
|
|
|
7
7
|
var objectAssign_1 = require("../utils/lang/objectAssign");
|
|
8
8
|
var constants_2 = require("../logger/constants");
|
|
9
9
|
var consent_1 = require("../consent");
|
|
10
|
+
var telemetrySubmitter_1 = require("../sync/submitters/telemetrySubmitter");
|
|
10
11
|
// 'unload' event is used instead of 'beforeunload', since 'unload' is not a cancelable event, so no other listeners can stop the event from occurring.
|
|
11
12
|
var UNLOAD_DOM_EVENT = 'unload';
|
|
12
13
|
var EVENT_NAME = 'for unload page event.';
|
|
@@ -63,7 +64,11 @@ var BrowserSignalListener = /** @class */ (function () {
|
|
|
63
64
|
this._flushData(eventsUrl + '/events/beacon', this.storage.events, this.serviceApi.postEventsBulk);
|
|
64
65
|
if (this.storage.impressionCounts)
|
|
65
66
|
this._flushData(eventsUrl + '/testImpressions/count/beacon', this.storage.impressionCounts, this.serviceApi.postTestImpressionsCount, impressionCountsSubmitter_1.fromImpressionCountsCollector);
|
|
66
|
-
|
|
67
|
+
if (this.storage.telemetry) {
|
|
68
|
+
var telemetryUrl = this.settings.urls.telemetry;
|
|
69
|
+
var telemetryCacheAdapter = (0, telemetrySubmitter_1.telemetryCacheStatsAdapter)(this.storage.telemetry, this.storage.splits, this.storage.segments);
|
|
70
|
+
this._flushData(telemetryUrl + '/v1/metrics/usage/beacon', telemetryCacheAdapter, this.serviceApi.postMetricsUsage);
|
|
71
|
+
}
|
|
67
72
|
}
|
|
68
73
|
// Close streaming connection
|
|
69
74
|
if (this.syncManager.pushManager)
|
|
@@ -13,15 +13,13 @@ var REMOVE_LISTENER_EVENT = 'removeListener';
|
|
|
13
13
|
* It also updates logs related warnings and errors.
|
|
14
14
|
*
|
|
15
15
|
* @param readyTimeout time in millis to emit SDK_READY_TIME_OUT event
|
|
16
|
-
* @param internalReadyCbCount offset value of SDK_READY listeners that are added/removed internally
|
|
17
|
-
* by the SDK. It is required to properly log the warning 'No listeners for SDK Readiness detected'
|
|
18
16
|
* @param readinessManager optional readinessManager to use. only used internally for `shared` method
|
|
19
17
|
*/
|
|
20
|
-
function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout,
|
|
18
|
+
function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, readinessManager) {
|
|
21
19
|
if (readyTimeout === void 0) { readyTimeout = 0; }
|
|
22
|
-
if (internalReadyCbCount === void 0) { internalReadyCbCount = 0; }
|
|
23
20
|
if (readinessManager === void 0) { readinessManager = (0, readinessManager_1.readinessManagerFactory)(EventEmitter, readyTimeout); }
|
|
24
21
|
/** Ready callback warning */
|
|
22
|
+
var internalReadyCbCount = 0;
|
|
25
23
|
var readyCbCount = 0;
|
|
26
24
|
readinessManager.gate.on(REMOVE_LISTENER_EVENT, function (event) {
|
|
27
25
|
if (event === constants_1.SDK_READY)
|
|
@@ -62,10 +60,12 @@ function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, internalRea
|
|
|
62
60
|
}
|
|
63
61
|
return {
|
|
64
62
|
readinessManager: readinessManager,
|
|
65
|
-
shared: function (readyTimeout
|
|
63
|
+
shared: function (readyTimeout) {
|
|
66
64
|
if (readyTimeout === void 0) { readyTimeout = 0; }
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
return sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, readinessManager.shared(readyTimeout));
|
|
66
|
+
},
|
|
67
|
+
incInternalReadyCbCount: function () {
|
|
68
|
+
internalReadyCbCount++;
|
|
69
69
|
},
|
|
70
70
|
sdkStatus: (0, objectAssign_1.objectAssign)(
|
|
71
71
|
// Expose Event Emitter functionality
|
package/cjs/services/splitApi.js
CHANGED
|
@@ -91,11 +91,11 @@ function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
91
91
|
return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.IMPRESSIONS_COUNT));
|
|
92
92
|
},
|
|
93
93
|
postMetricsConfig: function (body) {
|
|
94
|
-
var url = urls.telemetry + "/metrics/config";
|
|
94
|
+
var url = urls.telemetry + "/v1/metrics/config";
|
|
95
95
|
return splitHttpClient(url, { method: 'POST', body: body }, telemetryTracker.trackHttp(constants_1.TELEMETRY), true);
|
|
96
96
|
},
|
|
97
97
|
postMetricsUsage: function (body) {
|
|
98
|
-
var url = urls.telemetry + "/metrics/usage";
|
|
98
|
+
var url = urls.telemetry + "/v1/metrics/usage";
|
|
99
99
|
return splitHttpClient(url, { method: 'POST', body: body }, telemetryTracker.trackHttp(constants_1.TELEMETRY), true);
|
|
100
100
|
}
|
|
101
101
|
};
|
|
@@ -20,23 +20,27 @@ var KeyBuilderSS = /** @class */ (function (_super) {
|
|
|
20
20
|
KeyBuilderSS.prototype.buildRegisteredSegmentsKey = function () {
|
|
21
21
|
return this.prefix + ".segments.registered";
|
|
22
22
|
};
|
|
23
|
-
KeyBuilderSS.prototype.buildVersionablePrefix = function () {
|
|
24
|
-
return this.metadata.s + "/" + this.metadata.n + "/" + this.metadata.i;
|
|
25
|
-
};
|
|
26
23
|
KeyBuilderSS.prototype.buildImpressionsKey = function () {
|
|
27
24
|
return this.prefix + ".impressions";
|
|
28
25
|
};
|
|
29
26
|
KeyBuilderSS.prototype.buildEventsKey = function () {
|
|
30
27
|
return this.prefix + ".events";
|
|
31
28
|
};
|
|
29
|
+
KeyBuilderSS.prototype.searchPatternForSplitKeys = function () {
|
|
30
|
+
return this.buildSplitKeyPrefix() + "*";
|
|
31
|
+
};
|
|
32
|
+
/* Telemetry keys */
|
|
32
33
|
KeyBuilderSS.prototype.buildLatencyKey = function (method, bucket) {
|
|
33
34
|
return this.prefix + ".telemetry.latencies::" + this.buildVersionablePrefix() + "/" + methodNames[method] + "/" + bucket;
|
|
34
35
|
};
|
|
35
36
|
KeyBuilderSS.prototype.buildExceptionKey = function (method) {
|
|
36
37
|
return this.prefix + ".telemetry.exceptions::" + this.buildVersionablePrefix() + "/" + methodNames[method];
|
|
37
38
|
};
|
|
38
|
-
KeyBuilderSS.prototype.
|
|
39
|
-
return this.
|
|
39
|
+
KeyBuilderSS.prototype.buildInitKey = function () {
|
|
40
|
+
return this.prefix + ".telemetry.init::" + this.buildVersionablePrefix();
|
|
41
|
+
};
|
|
42
|
+
KeyBuilderSS.prototype.buildVersionablePrefix = function () {
|
|
43
|
+
return this.metadata.s + "/" + this.metadata.n + "/" + this.metadata.i;
|
|
40
44
|
};
|
|
41
45
|
return KeyBuilderSS;
|
|
42
46
|
}(KeyBuilder_1.KeyBuilder));
|
|
@@ -9,7 +9,7 @@ var thenable_1 = require("../../utils/promise/thenable");
|
|
|
9
9
|
var timeout_1 = require("../../utils/promise/timeout");
|
|
10
10
|
var LOG_PREFIX = 'storage:redis-adapter: ';
|
|
11
11
|
// If we ever decide to fully wrap every method, there's a Commander.getBuiltinCommands from ioredis.
|
|
12
|
-
var METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', 'pipeline', 'expire', 'mget', 'lrange', 'ltrim'];
|
|
12
|
+
var METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', 'pipeline', 'expire', 'mget', 'lrange', 'ltrim', 'hset'];
|
|
13
13
|
// Not part of the settings since it'll vary on each storage. We should be removing storage specific logic from elsewhere.
|
|
14
14
|
var DEFAULT_OPTIONS = {
|
|
15
15
|
connectionTimeout: 10000,
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TelemetryCacheInRedis = void 0;
|
|
4
4
|
var findLatencyIndex_1 = require("../findLatencyIndex");
|
|
5
|
+
var telemetrySubmitter_1 = require("../../sync/submitters/telemetrySubmitter");
|
|
6
|
+
var constants_1 = require("../../utils/constants");
|
|
5
7
|
var TelemetryCacheInRedis = /** @class */ (function () {
|
|
6
8
|
/**
|
|
7
9
|
* Create a Telemetry cache that uses Redis as storage.
|
|
@@ -24,6 +26,11 @@ var TelemetryCacheInRedis = /** @class */ (function () {
|
|
|
24
26
|
return this.redis.hincrby(key, field, 1)
|
|
25
27
|
.catch(function () { });
|
|
26
28
|
};
|
|
29
|
+
TelemetryCacheInRedis.prototype.recordConfig = function () {
|
|
30
|
+
var _a = this.keys.buildInitKey().split('::'), key = _a[0], field = _a[1];
|
|
31
|
+
var value = JSON.stringify((0, telemetrySubmitter_1.getTelemetryConfigStats)(constants_1.CONSUMER_MODE, constants_1.STORAGE_REDIS));
|
|
32
|
+
return this.redis.hset(key, field, value).catch(function () { });
|
|
33
|
+
};
|
|
27
34
|
return TelemetryCacheInRedis;
|
|
28
35
|
}());
|
|
29
36
|
exports.TelemetryCacheInRedis = TelemetryCacheInRedis;
|
|
@@ -21,16 +21,19 @@ function InRedisStorage(options) {
|
|
|
21
21
|
var log = _a.log, metadata = _a.metadata, onReadyCb = _a.onReadyCb;
|
|
22
22
|
var keys = new KeyBuilderSS_1.KeyBuilderSS(prefix, metadata);
|
|
23
23
|
var redisClient = new RedisAdapter_1.RedisAdapter(log, options.options || {});
|
|
24
|
+
var telemetry = new TelemetryCacheInRedis_1.TelemetryCacheInRedis(log, keys, redisClient);
|
|
24
25
|
// subscription to Redis connect event in order to emit SDK_READY event on consumer mode
|
|
25
26
|
redisClient.on('connect', function () {
|
|
26
27
|
onReadyCb();
|
|
28
|
+
// Synchronize config
|
|
29
|
+
telemetry.recordConfig();
|
|
27
30
|
});
|
|
28
31
|
return {
|
|
29
32
|
splits: new SplitsCacheInRedis_1.SplitsCacheInRedis(log, keys, redisClient),
|
|
30
33
|
segments: new SegmentsCacheInRedis_1.SegmentsCacheInRedis(log, keys, redisClient),
|
|
31
34
|
impressions: new ImpressionsCacheInRedis_1.ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
|
|
32
35
|
events: new EventsCacheInRedis_1.EventsCacheInRedis(log, keys.buildEventsKey(), redisClient, metadata),
|
|
33
|
-
telemetry:
|
|
36
|
+
telemetry: telemetry,
|
|
34
37
|
// When using REDIS we should:
|
|
35
38
|
// 1- Disconnect from the storage
|
|
36
39
|
destroy: function () {
|
|
@@ -34,6 +34,7 @@ function validatePluggableStorageOptions(options) {
|
|
|
34
34
|
function wrapperConnect(wrapper, onReadyCb) {
|
|
35
35
|
wrapper.connect().then(function () {
|
|
36
36
|
onReadyCb();
|
|
37
|
+
// At the moment, we don't synchronize config with pluggable storage
|
|
37
38
|
}).catch(function (e) {
|
|
38
39
|
onReadyCb(e || new Error('Error connecting wrapper'));
|
|
39
40
|
});
|
|
@@ -66,7 +67,7 @@ function PluggableStorage(options) {
|
|
|
66
67
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory_1.ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable_1.ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
67
68
|
impressionCounts: optimize ? new ImpressionCountsCacheInMemory_1.ImpressionCountsCacheInMemory() : undefined,
|
|
68
69
|
events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory_1.EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable_1.EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
|
|
69
|
-
// @TODO Not using TelemetryCachePluggable yet
|
|
70
|
+
// @TODO Not using TelemetryCachePluggable yet because it's not supported by the Split Synchronizer, and needs to drop or queue operations while the wrapper is not ready
|
|
70
71
|
// telemetry: isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper),
|
|
71
72
|
// Disconnect the underlying storage
|
|
72
73
|
destroy: function () {
|
|
@@ -6,7 +6,8 @@ var constants_1 = require("../../logger/constants");
|
|
|
6
6
|
/**
|
|
7
7
|
* Base function to create submitters, such as ImpressionsSubmitter and EventsSubmitter
|
|
8
8
|
*/
|
|
9
|
-
function submitterFactory(log, postClient, sourceCache, postRate, dataName, fromCacheToPayload, maxRetries, debugLogs
|
|
9
|
+
function submitterFactory(log, postClient, sourceCache, postRate, dataName, fromCacheToPayload, maxRetries, debugLogs // true for telemetry submitters
|
|
10
|
+
) {
|
|
10
11
|
if (maxRetries === void 0) { maxRetries = 0; }
|
|
11
12
|
var retries = 0;
|
|
12
13
|
function postData() {
|
|
@@ -24,16 +25,16 @@ function submitterFactory(log, postClient, sourceCache, postRate, dataName, from
|
|
|
24
25
|
sourceCache.clear(); // we clear the queue if request successes.
|
|
25
26
|
}).catch(function (err) {
|
|
26
27
|
if (!maxRetries) {
|
|
27
|
-
log
|
|
28
|
+
log[debugLogs ? 'debug' : 'warn'](constants_1.SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
|
|
28
29
|
}
|
|
29
30
|
else if (retries === maxRetries) {
|
|
30
31
|
retries = 0;
|
|
31
32
|
sourceCache.clear(); // we clear the queue if request fails after retries.
|
|
32
|
-
log
|
|
33
|
+
log[debugLogs ? 'debug' : 'warn'](constants_1.SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
|
|
33
34
|
}
|
|
34
35
|
else {
|
|
35
36
|
retries++;
|
|
36
|
-
log
|
|
37
|
+
log[debugLogs ? 'debug' : 'warn'](constants_1.SUBMITTERS_PUSH_RETRY, [dataCountMessage, err]);
|
|
37
38
|
}
|
|
38
39
|
});
|
|
39
40
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var _a, _b;
|
|
2
|
+
var _a, _b, _c;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.telemetrySubmitterFactory = exports.telemetryCacheConfigAdapter = exports.telemetryCacheStatsAdapter = void 0;
|
|
4
|
+
exports.telemetrySubmitterFactory = exports.telemetryCacheConfigAdapter = exports.getTelemetryConfigStats = exports.telemetryCacheStatsAdapter = void 0;
|
|
5
5
|
var submitter_1 = require("./submitter");
|
|
6
6
|
var constants_1 = require("../../utils/constants");
|
|
7
7
|
var constants_2 = require("../../readiness/constants");
|
|
8
8
|
var settingsValidation_1 = require("../../utils/settingsValidation");
|
|
9
9
|
var apiKey_1 = require("../../utils/inputValidation/apiKey");
|
|
10
10
|
var timer_1 = require("../../utils/timeTracker/timer");
|
|
11
|
+
var objectAssign_1 = require("../../utils/lang/objectAssign");
|
|
11
12
|
/**
|
|
12
13
|
* Converts data from telemetry cache into /metrics/usage request payload.
|
|
13
14
|
*/
|
|
@@ -50,6 +51,11 @@ var IMPRESSIONS_MODE_MAP = (_b = {},
|
|
|
50
51
|
_b[constants_1.OPTIMIZED] = constants_1.OPTIMIZED_ENUM,
|
|
51
52
|
_b[constants_1.DEBUG] = constants_1.DEBUG_ENUM,
|
|
52
53
|
_b);
|
|
54
|
+
var USER_CONSENT_MAP = (_c = {},
|
|
55
|
+
_c[constants_1.CONSENT_UNKNOWN] = 1,
|
|
56
|
+
_c[constants_1.CONSENT_GRANTED] = 2,
|
|
57
|
+
_c[constants_1.CONSENT_DECLINED] = 3,
|
|
58
|
+
_c);
|
|
53
59
|
function getActiveFactories() {
|
|
54
60
|
return Object.keys(apiKey_1.usedKeysMap).length;
|
|
55
61
|
}
|
|
@@ -58,6 +64,15 @@ function getRedundantActiveFactories() {
|
|
|
58
64
|
return acum + apiKey_1.usedKeysMap[apiKey] - 1;
|
|
59
65
|
}, 0);
|
|
60
66
|
}
|
|
67
|
+
function getTelemetryConfigStats(mode, storageType) {
|
|
68
|
+
return {
|
|
69
|
+
oM: OPERATION_MODE_MAP[mode],
|
|
70
|
+
st: storageType.toLowerCase(),
|
|
71
|
+
aF: getActiveFactories(),
|
|
72
|
+
rF: getRedundantActiveFactories(),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
exports.getTelemetryConfigStats = getTelemetryConfigStats;
|
|
61
76
|
/**
|
|
62
77
|
* Converts data from telemetry cache and settings into /metrics/config request payload.
|
|
63
78
|
*/
|
|
@@ -67,9 +82,7 @@ function telemetryCacheConfigAdapter(telemetry, settings) {
|
|
|
67
82
|
clear: function () { },
|
|
68
83
|
state: function () {
|
|
69
84
|
var urls = settings.urls, scheduler = settings.scheduler;
|
|
70
|
-
return {
|
|
71
|
-
oM: OPERATION_MODE_MAP[settings.mode],
|
|
72
|
-
st: settings.storage.type.toLowerCase(),
|
|
85
|
+
return (0, objectAssign_1.objectAssign)(getTelemetryConfigStats(settings.mode, settings.storage.type), {
|
|
73
86
|
sE: settings.streamingEnabled,
|
|
74
87
|
rR: {
|
|
75
88
|
sp: scheduler.featuresRefreshRate,
|
|
@@ -90,14 +103,13 @@ function telemetryCacheConfigAdapter(telemetry, settings) {
|
|
|
90
103
|
iM: IMPRESSIONS_MODE_MAP[settings.sync.impressionsMode],
|
|
91
104
|
iL: settings.impressionListener ? true : false,
|
|
92
105
|
hP: false,
|
|
93
|
-
aF: getActiveFactories(),
|
|
94
|
-
rF: getRedundantActiveFactories(),
|
|
95
106
|
tR: telemetry.getTimeUntilReady(),
|
|
96
107
|
tC: telemetry.getTimeUntilReadyFromCache(),
|
|
97
108
|
nR: telemetry.getNonReadyUsage(),
|
|
98
109
|
t: telemetry.popTags(),
|
|
99
110
|
i: settings.integrations && settings.integrations.map(function (int) { return int.type; }),
|
|
100
|
-
|
|
111
|
+
uC: settings.userConsent ? USER_CONSENT_MAP[settings.userConsent] : 0
|
|
112
|
+
});
|
|
101
113
|
}
|
|
102
114
|
};
|
|
103
115
|
}
|
|
@@ -109,12 +121,13 @@ function telemetrySubmitterFactory(params) {
|
|
|
109
121
|
var _a = params.storage, splits = _a.splits, segments = _a.segments, telemetry = _a.telemetry;
|
|
110
122
|
if (!telemetry)
|
|
111
123
|
return; // No submitter created if telemetry cache is not defined
|
|
112
|
-
var settings = params.settings, _b = params.settings, log = _b.log, telemetryRefreshRate = _b.scheduler.telemetryRefreshRate, splitApi = params.splitApi, now = params.platform.now, readiness = params.readiness;
|
|
124
|
+
var settings = params.settings, _b = params.settings, log = _b.log, telemetryRefreshRate = _b.scheduler.telemetryRefreshRate, splitApi = params.splitApi, now = params.platform.now, readiness = params.readiness, sdkReadinessManager = params.sdkReadinessManager;
|
|
113
125
|
var startTime = (0, timer_1.timer)(now || Date.now);
|
|
114
126
|
var submitter = (0, submitter_1.firstPushWindowDecorator)((0, submitter_1.submitterFactory)(log, splitApi.postMetricsUsage, telemetryCacheStatsAdapter(telemetry, splits, segments), telemetryRefreshRate, 'telemetry stats', undefined, 0, true), telemetryRefreshRate);
|
|
115
127
|
readiness.gate.once(constants_2.SDK_READY_FROM_CACHE, function () {
|
|
116
128
|
telemetry.recordTimeUntilReadyFromCache(startTime());
|
|
117
129
|
});
|
|
130
|
+
sdkReadinessManager.incInternalReadyCbCount();
|
|
118
131
|
readiness.gate.once(constants_2.SDK_READY, function () {
|
|
119
132
|
telemetry.recordTimeUntilReady(startTime());
|
|
120
133
|
// Post config data when the SDK is ready and if the telemetry submitter was started
|
|
@@ -32,8 +32,8 @@ exports.base = {
|
|
|
32
32
|
segmentsRefreshRate: 60,
|
|
33
33
|
// publish telemetry stats each 3600 secs (1 hour)
|
|
34
34
|
telemetryRefreshRate: 3600,
|
|
35
|
-
// publish evaluations each
|
|
36
|
-
impressionsRefreshRate:
|
|
35
|
+
// publish evaluations each 300 sec (default value for OPTIMIZED impressions mode)
|
|
36
|
+
impressionsRefreshRate: 300,
|
|
37
37
|
// fetch offline changes each 15 sec
|
|
38
38
|
offlineRefreshRate: 15,
|
|
39
39
|
// publish events every 60 seconds after the first flush
|
|
@@ -55,7 +55,7 @@ exports.base = {
|
|
|
55
55
|
// Streaming Server
|
|
56
56
|
streaming: 'https://streaming.split.io',
|
|
57
57
|
// Telemetry Server
|
|
58
|
-
telemetry: 'https://telemetry.split.io',
|
|
58
|
+
telemetry: 'https://telemetry.split.io/api',
|
|
59
59
|
},
|
|
60
60
|
// Defines which kind of storage we should instanciate.
|
|
61
61
|
storage: undefined,
|
|
@@ -96,6 +96,8 @@ function settingsValidation(config, validationParams) {
|
|
|
96
96
|
// First thing to validate, since other validators might use the logger.
|
|
97
97
|
var log = logger(withDefaults); // @ts-ignore, modify readonly prop
|
|
98
98
|
withDefaults.log = log;
|
|
99
|
+
// ensure a valid impressionsMode
|
|
100
|
+
withDefaults.sync.impressionsMode = (0, impressionsMode_1.validImpressionsMode)(log, withDefaults.sync.impressionsMode);
|
|
99
101
|
function validateMinValue(paramName, actualValue, minValue) {
|
|
100
102
|
if (actualValue >= minValue)
|
|
101
103
|
return actualValue;
|
|
@@ -107,10 +109,20 @@ function settingsValidation(config, validationParams) {
|
|
|
107
109
|
var scheduler = withDefaults.scheduler, startup = withDefaults.startup;
|
|
108
110
|
scheduler.featuresRefreshRate = fromSecondsToMillis(scheduler.featuresRefreshRate);
|
|
109
111
|
scheduler.segmentsRefreshRate = fromSecondsToMillis(scheduler.segmentsRefreshRate);
|
|
110
|
-
scheduler.impressionsRefreshRate = fromSecondsToMillis(scheduler.impressionsRefreshRate);
|
|
111
112
|
scheduler.offlineRefreshRate = fromSecondsToMillis(scheduler.offlineRefreshRate);
|
|
112
113
|
scheduler.eventsPushRate = fromSecondsToMillis(scheduler.eventsPushRate);
|
|
113
114
|
scheduler.telemetryRefreshRate = fromSecondsToMillis(validateMinValue('telemetryRefreshRate', scheduler.telemetryRefreshRate, 60));
|
|
115
|
+
if (scheduler.impressionsRefreshRate !== exports.base.scheduler.impressionsRefreshRate) {
|
|
116
|
+
// Validate impressionsRefreshRate defined by user
|
|
117
|
+
scheduler.impressionsRefreshRate = validateMinValue('impressionsRefreshRate', scheduler.impressionsRefreshRate, withDefaults.sync.impressionsMode === constants_1.DEBUG ? 1 : 60 // Min is 1 sec for DEBUG and 60 secs for OPTIMIZED
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Default impressionsRefreshRate for DEBUG mode is 60 secs
|
|
122
|
+
if (withDefaults.sync.impressionsMode === constants_1.DEBUG)
|
|
123
|
+
scheduler.impressionsRefreshRate = 60;
|
|
124
|
+
}
|
|
125
|
+
scheduler.impressionsRefreshRate = fromSecondsToMillis(scheduler.impressionsRefreshRate);
|
|
114
126
|
// Log deprecation for old telemetry param
|
|
115
127
|
if (scheduler.metricsRefreshRate)
|
|
116
128
|
log.warn('`metricsRefreshRate` will be deprecated soon. For configuring telemetry rates, update `telemetryRefreshRate` value in configs');
|
|
@@ -166,8 +178,6 @@ function settingsValidation(config, validationParams) {
|
|
|
166
178
|
var splitFiltersValidation = (0, splitFilters_1.validateSplitFilters)(log, withDefaults.sync.splitFilters, withDefaults.mode);
|
|
167
179
|
withDefaults.sync.splitFilters = splitFiltersValidation.validFilters;
|
|
168
180
|
withDefaults.sync.__splitFiltersValidation = splitFiltersValidation;
|
|
169
|
-
// ensure a valid impressionsMode
|
|
170
|
-
withDefaults.sync.impressionsMode = (0, impressionsMode_1.validImpressionsMode)(log, withDefaults.sync.impressionsMode);
|
|
171
181
|
// ensure a valid user consent value
|
|
172
182
|
// @ts-ignore, modify readonly prop
|
|
173
183
|
withDefaults.userConsent = consent(withDefaults);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.url = void 0;
|
|
4
|
-
var telemetryEndpointMatcher = /^\/metrics\/(config|usage)/;
|
|
4
|
+
var telemetryEndpointMatcher = /^\/v1\/metrics\/(config|usage)/;
|
|
5
5
|
var eventsEndpointMatcher = /^\/(testImpressions|metrics|events)/;
|
|
6
6
|
var authEndpointMatcher = /^\/v2\/auth/;
|
|
7
7
|
var streamingEndpointMatcher = /^\/(sse|event-stream)/;
|
package/esm/listeners/browser.js
CHANGED
|
@@ -4,6 +4,7 @@ import { OPTIMIZED, DEBUG } from '../utils/constants';
|
|
|
4
4
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
5
5
|
import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
|
|
6
6
|
import { isConsentGranted } from '../consent';
|
|
7
|
+
import { telemetryCacheStatsAdapter } from '../sync/submitters/telemetrySubmitter';
|
|
7
8
|
// 'unload' event is used instead of 'beforeunload', since 'unload' is not a cancelable event, so no other listeners can stop the event from occurring.
|
|
8
9
|
var UNLOAD_DOM_EVENT = 'unload';
|
|
9
10
|
var EVENT_NAME = 'for unload page event.';
|
|
@@ -60,7 +61,11 @@ var BrowserSignalListener = /** @class */ (function () {
|
|
|
60
61
|
this._flushData(eventsUrl + '/events/beacon', this.storage.events, this.serviceApi.postEventsBulk);
|
|
61
62
|
if (this.storage.impressionCounts)
|
|
62
63
|
this._flushData(eventsUrl + '/testImpressions/count/beacon', this.storage.impressionCounts, this.serviceApi.postTestImpressionsCount, fromImpressionCountsCollector);
|
|
63
|
-
|
|
64
|
+
if (this.storage.telemetry) {
|
|
65
|
+
var telemetryUrl = this.settings.urls.telemetry;
|
|
66
|
+
var telemetryCacheAdapter = telemetryCacheStatsAdapter(this.storage.telemetry, this.storage.splits, this.storage.segments);
|
|
67
|
+
this._flushData(telemetryUrl + '/v1/metrics/usage/beacon', telemetryCacheAdapter, this.serviceApi.postMetricsUsage);
|
|
68
|
+
}
|
|
64
69
|
}
|
|
65
70
|
// Close streaming connection
|
|
66
71
|
if (this.syncManager.pushManager)
|
|
@@ -10,15 +10,13 @@ var REMOVE_LISTENER_EVENT = 'removeListener';
|
|
|
10
10
|
* It also updates logs related warnings and errors.
|
|
11
11
|
*
|
|
12
12
|
* @param readyTimeout time in millis to emit SDK_READY_TIME_OUT event
|
|
13
|
-
* @param internalReadyCbCount offset value of SDK_READY listeners that are added/removed internally
|
|
14
|
-
* by the SDK. It is required to properly log the warning 'No listeners for SDK Readiness detected'
|
|
15
13
|
* @param readinessManager optional readinessManager to use. only used internally for `shared` method
|
|
16
14
|
*/
|
|
17
|
-
export function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout,
|
|
15
|
+
export function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, readinessManager) {
|
|
18
16
|
if (readyTimeout === void 0) { readyTimeout = 0; }
|
|
19
|
-
if (internalReadyCbCount === void 0) { internalReadyCbCount = 0; }
|
|
20
17
|
if (readinessManager === void 0) { readinessManager = readinessManagerFactory(EventEmitter, readyTimeout); }
|
|
21
18
|
/** Ready callback warning */
|
|
19
|
+
var internalReadyCbCount = 0;
|
|
22
20
|
var readyCbCount = 0;
|
|
23
21
|
readinessManager.gate.on(REMOVE_LISTENER_EVENT, function (event) {
|
|
24
22
|
if (event === SDK_READY)
|
|
@@ -59,10 +57,12 @@ export function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, inte
|
|
|
59
57
|
}
|
|
60
58
|
return {
|
|
61
59
|
readinessManager: readinessManager,
|
|
62
|
-
shared: function (readyTimeout
|
|
60
|
+
shared: function (readyTimeout) {
|
|
63
61
|
if (readyTimeout === void 0) { readyTimeout = 0; }
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
return sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, readinessManager.shared(readyTimeout));
|
|
63
|
+
},
|
|
64
|
+
incInternalReadyCbCount: function () {
|
|
65
|
+
internalReadyCbCount++;
|
|
66
66
|
},
|
|
67
67
|
sdkStatus: objectAssign(
|
|
68
68
|
// Expose Event Emitter functionality
|
package/esm/services/splitApi.js
CHANGED
|
@@ -88,11 +88,11 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
88
88
|
return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT));
|
|
89
89
|
},
|
|
90
90
|
postMetricsConfig: function (body) {
|
|
91
|
-
var url = urls.telemetry + "/metrics/config";
|
|
91
|
+
var url = urls.telemetry + "/v1/metrics/config";
|
|
92
92
|
return splitHttpClient(url, { method: 'POST', body: body }, telemetryTracker.trackHttp(TELEMETRY), true);
|
|
93
93
|
},
|
|
94
94
|
postMetricsUsage: function (body) {
|
|
95
|
-
var url = urls.telemetry + "/metrics/usage";
|
|
95
|
+
var url = urls.telemetry + "/v1/metrics/usage";
|
|
96
96
|
return splitHttpClient(url, { method: 'POST', body: body }, telemetryTracker.trackHttp(TELEMETRY), true);
|
|
97
97
|
}
|
|
98
98
|
};
|
|
@@ -17,23 +17,27 @@ var KeyBuilderSS = /** @class */ (function (_super) {
|
|
|
17
17
|
KeyBuilderSS.prototype.buildRegisteredSegmentsKey = function () {
|
|
18
18
|
return this.prefix + ".segments.registered";
|
|
19
19
|
};
|
|
20
|
-
KeyBuilderSS.prototype.buildVersionablePrefix = function () {
|
|
21
|
-
return this.metadata.s + "/" + this.metadata.n + "/" + this.metadata.i;
|
|
22
|
-
};
|
|
23
20
|
KeyBuilderSS.prototype.buildImpressionsKey = function () {
|
|
24
21
|
return this.prefix + ".impressions";
|
|
25
22
|
};
|
|
26
23
|
KeyBuilderSS.prototype.buildEventsKey = function () {
|
|
27
24
|
return this.prefix + ".events";
|
|
28
25
|
};
|
|
26
|
+
KeyBuilderSS.prototype.searchPatternForSplitKeys = function () {
|
|
27
|
+
return this.buildSplitKeyPrefix() + "*";
|
|
28
|
+
};
|
|
29
|
+
/* Telemetry keys */
|
|
29
30
|
KeyBuilderSS.prototype.buildLatencyKey = function (method, bucket) {
|
|
30
31
|
return this.prefix + ".telemetry.latencies::" + this.buildVersionablePrefix() + "/" + methodNames[method] + "/" + bucket;
|
|
31
32
|
};
|
|
32
33
|
KeyBuilderSS.prototype.buildExceptionKey = function (method) {
|
|
33
34
|
return this.prefix + ".telemetry.exceptions::" + this.buildVersionablePrefix() + "/" + methodNames[method];
|
|
34
35
|
};
|
|
35
|
-
KeyBuilderSS.prototype.
|
|
36
|
-
return this.
|
|
36
|
+
KeyBuilderSS.prototype.buildInitKey = function () {
|
|
37
|
+
return this.prefix + ".telemetry.init::" + this.buildVersionablePrefix();
|
|
38
|
+
};
|
|
39
|
+
KeyBuilderSS.prototype.buildVersionablePrefix = function () {
|
|
40
|
+
return this.metadata.s + "/" + this.metadata.n + "/" + this.metadata.i;
|
|
37
41
|
};
|
|
38
42
|
return KeyBuilderSS;
|
|
39
43
|
}(KeyBuilder));
|
|
@@ -6,7 +6,7 @@ import { thenable } from '../../utils/promise/thenable';
|
|
|
6
6
|
import { timeout } from '../../utils/promise/timeout';
|
|
7
7
|
var LOG_PREFIX = 'storage:redis-adapter: ';
|
|
8
8
|
// If we ever decide to fully wrap every method, there's a Commander.getBuiltinCommands from ioredis.
|
|
9
|
-
var METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', 'pipeline', 'expire', 'mget', 'lrange', 'ltrim'];
|
|
9
|
+
var METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', 'pipeline', 'expire', 'mget', 'lrange', 'ltrim', 'hset'];
|
|
10
10
|
// Not part of the settings since it'll vary on each storage. We should be removing storage specific logic from elsewhere.
|
|
11
11
|
var DEFAULT_OPTIONS = {
|
|
12
12
|
connectionTimeout: 10000,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { findLatencyIndex } from '../findLatencyIndex';
|
|
2
|
+
import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter';
|
|
3
|
+
import { CONSUMER_MODE, STORAGE_REDIS } from '../../utils/constants';
|
|
2
4
|
var TelemetryCacheInRedis = /** @class */ (function () {
|
|
3
5
|
/**
|
|
4
6
|
* Create a Telemetry cache that uses Redis as storage.
|
|
@@ -21,6 +23,11 @@ var TelemetryCacheInRedis = /** @class */ (function () {
|
|
|
21
23
|
return this.redis.hincrby(key, field, 1)
|
|
22
24
|
.catch(function () { });
|
|
23
25
|
};
|
|
26
|
+
TelemetryCacheInRedis.prototype.recordConfig = function () {
|
|
27
|
+
var _a = this.keys.buildInitKey().split('::'), key = _a[0], field = _a[1];
|
|
28
|
+
var value = JSON.stringify(getTelemetryConfigStats(CONSUMER_MODE, STORAGE_REDIS));
|
|
29
|
+
return this.redis.hset(key, field, value).catch(function () { });
|
|
30
|
+
};
|
|
24
31
|
return TelemetryCacheInRedis;
|
|
25
32
|
}());
|
|
26
33
|
export { TelemetryCacheInRedis };
|
|
@@ -18,16 +18,19 @@ export function InRedisStorage(options) {
|
|
|
18
18
|
var log = _a.log, metadata = _a.metadata, onReadyCb = _a.onReadyCb;
|
|
19
19
|
var keys = new KeyBuilderSS(prefix, metadata);
|
|
20
20
|
var redisClient = new RedisAdapter(log, options.options || {});
|
|
21
|
+
var telemetry = new TelemetryCacheInRedis(log, keys, redisClient);
|
|
21
22
|
// subscription to Redis connect event in order to emit SDK_READY event on consumer mode
|
|
22
23
|
redisClient.on('connect', function () {
|
|
23
24
|
onReadyCb();
|
|
25
|
+
// Synchronize config
|
|
26
|
+
telemetry.recordConfig();
|
|
24
27
|
});
|
|
25
28
|
return {
|
|
26
29
|
splits: new SplitsCacheInRedis(log, keys, redisClient),
|
|
27
30
|
segments: new SegmentsCacheInRedis(log, keys, redisClient),
|
|
28
31
|
impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
|
|
29
32
|
events: new EventsCacheInRedis(log, keys.buildEventsKey(), redisClient, metadata),
|
|
30
|
-
telemetry:
|
|
33
|
+
telemetry: telemetry,
|
|
31
34
|
// When using REDIS we should:
|
|
32
35
|
// 1- Disconnect from the storage
|
|
33
36
|
destroy: function () {
|
|
@@ -31,6 +31,7 @@ function validatePluggableStorageOptions(options) {
|
|
|
31
31
|
function wrapperConnect(wrapper, onReadyCb) {
|
|
32
32
|
wrapper.connect().then(function () {
|
|
33
33
|
onReadyCb();
|
|
34
|
+
// At the moment, we don't synchronize config with pluggable storage
|
|
34
35
|
}).catch(function (e) {
|
|
35
36
|
onReadyCb(e || new Error('Error connecting wrapper'));
|
|
36
37
|
});
|
|
@@ -63,7 +64,7 @@ export function PluggableStorage(options) {
|
|
|
63
64
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
64
65
|
impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined,
|
|
65
66
|
events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
|
|
66
|
-
// @TODO Not using TelemetryCachePluggable yet
|
|
67
|
+
// @TODO Not using TelemetryCachePluggable yet because it's not supported by the Split Synchronizer, and needs to drop or queue operations while the wrapper is not ready
|
|
67
68
|
// telemetry: isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper),
|
|
68
69
|
// Disconnect the underlying storage
|
|
69
70
|
destroy: function () {
|
|
@@ -3,7 +3,8 @@ import { SUBMITTERS_PUSH, SUBMITTERS_PUSH_FAILS, SUBMITTERS_PUSH_RETRY } from '.
|
|
|
3
3
|
/**
|
|
4
4
|
* Base function to create submitters, such as ImpressionsSubmitter and EventsSubmitter
|
|
5
5
|
*/
|
|
6
|
-
export function submitterFactory(log, postClient, sourceCache, postRate, dataName, fromCacheToPayload, maxRetries, debugLogs
|
|
6
|
+
export function submitterFactory(log, postClient, sourceCache, postRate, dataName, fromCacheToPayload, maxRetries, debugLogs // true for telemetry submitters
|
|
7
|
+
) {
|
|
7
8
|
if (maxRetries === void 0) { maxRetries = 0; }
|
|
8
9
|
var retries = 0;
|
|
9
10
|
function postData() {
|
|
@@ -21,16 +22,16 @@ export function submitterFactory(log, postClient, sourceCache, postRate, dataNam
|
|
|
21
22
|
sourceCache.clear(); // we clear the queue if request successes.
|
|
22
23
|
}).catch(function (err) {
|
|
23
24
|
if (!maxRetries) {
|
|
24
|
-
log
|
|
25
|
+
log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
|
|
25
26
|
}
|
|
26
27
|
else if (retries === maxRetries) {
|
|
27
28
|
retries = 0;
|
|
28
29
|
sourceCache.clear(); // we clear the queue if request fails after retries.
|
|
29
|
-
log
|
|
30
|
+
log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
|
|
30
31
|
}
|
|
31
32
|
else {
|
|
32
33
|
retries++;
|
|
33
|
-
log
|
|
34
|
+
log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_RETRY, [dataCountMessage, err]);
|
|
34
35
|
}
|
|
35
36
|
});
|
|
36
37
|
}
|