@splitsoftware/splitio-commons 1.6.2-rc.13 → 1.6.2-rc.14
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/storages/KeyBuilderSS.js +4 -43
- package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +3 -3
- package/cjs/storages/inRedis/ImpressionsCacheInRedis.js +2 -19
- package/cjs/storages/inRedis/TelemetryCacheInRedis.js +4 -4
- package/cjs/storages/inRedis/index.js +2 -2
- package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +3 -3
- package/cjs/storages/pluggable/ImpressionsCachePluggable.js +2 -19
- package/cjs/storages/pluggable/TelemetryCachePluggable.js +4 -4
- package/cjs/storages/pluggable/inMemoryWrapper.js +8 -6
- package/cjs/storages/pluggable/index.js +2 -2
- package/cjs/storages/utils.js +73 -0
- package/esm/storages/KeyBuilderSS.js +1 -37
- package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +3 -3
- package/esm/storages/inRedis/ImpressionsCacheInRedis.js +2 -19
- package/esm/storages/inRedis/TelemetryCacheInRedis.js +1 -1
- package/esm/storages/inRedis/index.js +1 -1
- package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +3 -3
- package/esm/storages/pluggable/ImpressionsCachePluggable.js +2 -19
- package/esm/storages/pluggable/TelemetryCachePluggable.js +1 -1
- package/esm/storages/pluggable/inMemoryWrapper.js +8 -6
- package/esm/storages/pluggable/index.js +1 -1
- package/esm/storages/utils.js +65 -0
- package/package.json +1 -1
- package/src/services/splitApi.ts +2 -2
- package/src/storages/KeyBuilderSS.ts +2 -44
- package/src/storages/inMemory/AttributesCacheInMemory.ts +7 -7
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/ImpressionsCacheInRedis.ts +2 -22
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +2 -1
- package/src/storages/inRedis/index.ts +1 -1
- package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +3 -3
- package/src/storages/pluggable/ImpressionsCachePluggable.ts +3 -23
- package/src/storages/pluggable/TelemetryCachePluggable.ts +2 -1
- package/src/storages/pluggable/inMemoryWrapper.ts +6 -6
- package/src/storages/pluggable/index.ts +1 -1
- package/src/storages/types.ts +4 -4
- package/src/storages/utils.ts +78 -0
- package/src/sync/submitters/types.ts +2 -0
- package/src/trackers/impressionsTracker.ts +2 -2
- package/src/trackers/strategy/strategyDebug.ts +4 -4
- package/src/utils/redis/RedisMock.ts +5 -5
- package/types/storages/KeyBuilderSS.d.ts +1 -3
- package/types/storages/inRedis/ImpressionsCacheInRedis.d.ts +0 -1
- package/types/storages/pluggable/ImpressionsCachePluggable.d.ts +1 -2
- package/types/storages/types.d.ts +4 -4
- package/types/storages/utils.d.ts +8 -0
- package/types/sync/submitters/types.d.ts +2 -0
- package/cjs/storages/metadataBuilder.js +0 -12
- package/esm/storages/metadataBuilder.js +0 -8
- package/src/storages/metadataBuilder.ts +0 -11
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.KeyBuilderSS = exports.METHOD_NAMES = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var KeyBuilder_1 = require("./KeyBuilder");
|
|
6
|
-
|
|
7
|
-
var METHOD_NAMES = {
|
|
6
|
+
exports.METHOD_NAMES = {
|
|
8
7
|
t: 'treatment',
|
|
9
8
|
ts: 'treatments',
|
|
10
9
|
tc: 'treatmentWithConfig',
|
|
@@ -41,10 +40,10 @@ var KeyBuilderSS = /** @class */ (function (_super) {
|
|
|
41
40
|
};
|
|
42
41
|
/* Telemetry keys */
|
|
43
42
|
KeyBuilderSS.prototype.buildLatencyKey = function (method, bucket) {
|
|
44
|
-
return this.latencyPrefix + "::" + this.versionablePrefix + "/" + METHOD_NAMES[method] + "/" + bucket;
|
|
43
|
+
return this.latencyPrefix + "::" + this.versionablePrefix + "/" + exports.METHOD_NAMES[method] + "/" + bucket;
|
|
45
44
|
};
|
|
46
45
|
KeyBuilderSS.prototype.buildExceptionKey = function (method) {
|
|
47
|
-
return this.exceptionPrefix + "::" + this.versionablePrefix + "/" + METHOD_NAMES[method];
|
|
46
|
+
return this.exceptionPrefix + "::" + this.versionablePrefix + "/" + exports.METHOD_NAMES[method];
|
|
48
47
|
};
|
|
49
48
|
KeyBuilderSS.prototype.buildInitKey = function () {
|
|
50
49
|
return this.initPrefix + "::" + this.versionablePrefix;
|
|
@@ -52,41 +51,3 @@ var KeyBuilderSS = /** @class */ (function (_super) {
|
|
|
52
51
|
return KeyBuilderSS;
|
|
53
52
|
}(KeyBuilder_1.KeyBuilder));
|
|
54
53
|
exports.KeyBuilderSS = KeyBuilderSS;
|
|
55
|
-
// Used by consumer methods of TelemetryCacheInRedis and TelemetryCachePluggable
|
|
56
|
-
var REVERSE_METHOD_NAMES = Object.keys(METHOD_NAMES).reduce(function (acc, key) {
|
|
57
|
-
acc[METHOD_NAMES[key]] = key;
|
|
58
|
-
return acc;
|
|
59
|
-
}, {});
|
|
60
|
-
function parseMetadata(field) {
|
|
61
|
-
var parts = field.split('/');
|
|
62
|
-
if (parts.length !== 3)
|
|
63
|
-
return "invalid subsection count. Expected 3, got: " + parts.length;
|
|
64
|
-
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */;
|
|
65
|
-
return [JSON.stringify({ s: s, n: n, i: i })];
|
|
66
|
-
}
|
|
67
|
-
exports.parseMetadata = parseMetadata;
|
|
68
|
-
function parseExceptionField(field) {
|
|
69
|
-
var parts = field.split('/');
|
|
70
|
-
if (parts.length !== 4)
|
|
71
|
-
return "invalid subsection count. Expected 4, got: " + parts.length;
|
|
72
|
-
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */, m = parts[3];
|
|
73
|
-
var method = REVERSE_METHOD_NAMES[m];
|
|
74
|
-
if (!method)
|
|
75
|
-
return "unknown method '" + m + "'";
|
|
76
|
-
return [JSON.stringify({ s: s, n: n, i: i }), method];
|
|
77
|
-
}
|
|
78
|
-
exports.parseExceptionField = parseExceptionField;
|
|
79
|
-
function parseLatencyField(field) {
|
|
80
|
-
var parts = field.split('/');
|
|
81
|
-
if (parts.length !== 5)
|
|
82
|
-
return "invalid subsection count. Expected 5, got: " + parts.length;
|
|
83
|
-
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */, m = parts[3], b = parts[4];
|
|
84
|
-
var method = REVERSE_METHOD_NAMES[m];
|
|
85
|
-
if (!method)
|
|
86
|
-
return "unknown method '" + m + "'";
|
|
87
|
-
var bucket = parseInt(b);
|
|
88
|
-
if (isNaN(bucket) || bucket >= TelemetryCacheInMemory_1.MAX_LATENCY_BUCKET_COUNT)
|
|
89
|
-
return "invalid bucket. Expected a number between 0 and " + (TelemetryCacheInMemory_1.MAX_LATENCY_BUCKET_COUNT - 1) + ", got: " + b;
|
|
90
|
-
return [JSON.stringify({ s: s, n: n, i: i }), method, bucket];
|
|
91
|
-
}
|
|
92
|
-
exports.parseLatencyField = parseLatencyField;
|
|
@@ -54,7 +54,7 @@ var ImpressionCountsCacheInRedis = /** @class */ (function (_super) {
|
|
|
54
54
|
if (!Object.keys(counts).length)
|
|
55
55
|
return undefined;
|
|
56
56
|
_this.redis.del(_this.key).catch(function () { });
|
|
57
|
-
var
|
|
57
|
+
var pf = [];
|
|
58
58
|
(0, lang_1.forOwn)(counts, function (count, key) {
|
|
59
59
|
var nameAndTime = key.split('::');
|
|
60
60
|
if (nameAndTime.length !== 2) {
|
|
@@ -71,13 +71,13 @@ var ImpressionCountsCacheInRedis = /** @class */ (function (_super) {
|
|
|
71
71
|
_this.log.error(constants_1.LOG_PREFIX + "Error parsing raw count " + count);
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
pf.push({
|
|
75
75
|
f: nameAndTime[0],
|
|
76
76
|
m: timeFrame,
|
|
77
77
|
rc: rawCount,
|
|
78
78
|
});
|
|
79
79
|
});
|
|
80
|
-
return
|
|
80
|
+
return { pf: pf };
|
|
81
81
|
});
|
|
82
82
|
};
|
|
83
83
|
return ImpressionCountsCacheInRedis;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ImpressionsCacheInRedis = void 0;
|
|
4
|
+
var utils_1 = require("../utils");
|
|
4
5
|
var IMPRESSIONS_TTL_REFRESH = 3600; // 1 hr
|
|
5
6
|
var ImpressionsCacheInRedis = /** @class */ (function () {
|
|
6
7
|
function ImpressionsCacheInRedis(log, key, redis, metadata) {
|
|
@@ -11,31 +12,13 @@ var ImpressionsCacheInRedis = /** @class */ (function () {
|
|
|
11
12
|
}
|
|
12
13
|
ImpressionsCacheInRedis.prototype.track = function (impressions) {
|
|
13
14
|
var _this = this;
|
|
14
|
-
return this.redis.rpush(this.key,
|
|
15
|
+
return this.redis.rpush(this.key, (0, utils_1.impressionsToJSON)(impressions, this.metadata)).then(function (queuedCount) {
|
|
15
16
|
// If this is the creation of the key on Redis, set the expiration for it in 1hr.
|
|
16
17
|
if (queuedCount === impressions.length) {
|
|
17
18
|
return _this.redis.expire(_this.key, IMPRESSIONS_TTL_REFRESH);
|
|
18
19
|
}
|
|
19
20
|
});
|
|
20
21
|
};
|
|
21
|
-
ImpressionsCacheInRedis.prototype._toJSON = function (impressions) {
|
|
22
|
-
var _this = this;
|
|
23
|
-
return impressions.map(function (impression) {
|
|
24
|
-
var keyName = impression.keyName, bucketingKey = impression.bucketingKey, feature = impression.feature, treatment = impression.treatment, label = impression.label, time = impression.time, changeNumber = impression.changeNumber;
|
|
25
|
-
return JSON.stringify({
|
|
26
|
-
m: _this.metadata,
|
|
27
|
-
i: {
|
|
28
|
-
k: keyName,
|
|
29
|
-
b: bucketingKey,
|
|
30
|
-
f: feature,
|
|
31
|
-
t: treatment,
|
|
32
|
-
r: label,
|
|
33
|
-
c: changeNumber,
|
|
34
|
-
m: time
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
22
|
ImpressionsCacheInRedis.prototype.count = function () {
|
|
40
23
|
return this.redis.llen(this.key).catch(function () { return 0; });
|
|
41
24
|
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TelemetryCacheInRedis = void 0;
|
|
4
|
-
var KeyBuilderSS_1 = require("../KeyBuilderSS");
|
|
5
4
|
var findLatencyIndex_1 = require("../findLatencyIndex");
|
|
6
5
|
var telemetrySubmitter_1 = require("../../sync/submitters/telemetrySubmitter");
|
|
7
6
|
var constants_1 = require("../../utils/constants");
|
|
8
7
|
var lang_1 = require("../../utils/lang");
|
|
9
8
|
var maps_1 = require("../../utils/lang/maps");
|
|
10
9
|
var TelemetryCacheInMemory_1 = require("../inMemory/TelemetryCacheInMemory");
|
|
10
|
+
var utils_1 = require("../utils");
|
|
11
11
|
var TelemetryCacheInRedis = /** @class */ (function () {
|
|
12
12
|
/**
|
|
13
13
|
* Create a Telemetry cache that uses Redis as storage.
|
|
@@ -44,7 +44,7 @@ var TelemetryCacheInRedis = /** @class */ (function () {
|
|
|
44
44
|
return this.redis.hgetall(this.keys.latencyPrefix).then(function (latencies) {
|
|
45
45
|
var result = new maps_1._Map();
|
|
46
46
|
Object.keys(latencies).forEach(function (field) {
|
|
47
|
-
var parsedField = (0,
|
|
47
|
+
var parsedField = (0, utils_1.parseLatencyField)(field);
|
|
48
48
|
if ((0, lang_1.isString)(parsedField)) {
|
|
49
49
|
_this.log.error("Ignoring invalid latency field: " + field + ": " + parsedField);
|
|
50
50
|
return;
|
|
@@ -81,7 +81,7 @@ var TelemetryCacheInRedis = /** @class */ (function () {
|
|
|
81
81
|
return this.redis.hgetall(this.keys.exceptionPrefix).then(function (exceptions) {
|
|
82
82
|
var result = new maps_1._Map();
|
|
83
83
|
Object.keys(exceptions).forEach(function (field) {
|
|
84
|
-
var parsedField = (0,
|
|
84
|
+
var parsedField = (0, utils_1.parseExceptionField)(field);
|
|
85
85
|
if ((0, lang_1.isString)(parsedField)) {
|
|
86
86
|
_this.log.error("Ignoring invalid exception field: " + field + ": " + parsedField);
|
|
87
87
|
return;
|
|
@@ -114,7 +114,7 @@ var TelemetryCacheInRedis = /** @class */ (function () {
|
|
|
114
114
|
return this.redis.hgetall(this.keys.initPrefix).then(function (configs) {
|
|
115
115
|
var result = new maps_1._Map();
|
|
116
116
|
Object.keys(configs).forEach(function (field) {
|
|
117
|
-
var parsedField = (0,
|
|
117
|
+
var parsedField = (0, utils_1.parseMetadata)(field);
|
|
118
118
|
if ((0, lang_1.isString)(parsedField)) {
|
|
119
119
|
_this.log.error("Ignoring invalid config field: " + field + ": " + parsedField);
|
|
120
120
|
return;
|
|
@@ -12,7 +12,7 @@ var constants_1 = require("../../utils/constants");
|
|
|
12
12
|
var TelemetryCacheInRedis_1 = require("./TelemetryCacheInRedis");
|
|
13
13
|
var UniqueKeysCacheInRedis_1 = require("./UniqueKeysCacheInRedis");
|
|
14
14
|
var ImpressionCountsCacheInRedis_1 = require("./ImpressionCountsCacheInRedis");
|
|
15
|
-
var
|
|
15
|
+
var utils_1 = require("../utils");
|
|
16
16
|
/**
|
|
17
17
|
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.
|
|
18
18
|
* @see {@link https://www.npmjs.com/package/ioredis}
|
|
@@ -22,7 +22,7 @@ function InRedisStorage(options) {
|
|
|
22
22
|
var prefix = (0, KeyBuilder_1.validatePrefix)(options.prefix);
|
|
23
23
|
function InRedisStorageFactory(params) {
|
|
24
24
|
var onReadyCb = params.onReadyCb, settings = params.settings, _a = params.settings, log = _a.log, impressionsMode = _a.sync.impressionsMode;
|
|
25
|
-
var metadata = (0,
|
|
25
|
+
var metadata = (0, utils_1.metadataBuilder)(settings);
|
|
26
26
|
var keys = new KeyBuilderSS_1.KeyBuilderSS(prefix, metadata);
|
|
27
27
|
var redisClient = new RedisAdapter_1.RedisAdapter(log, options.options || {});
|
|
28
28
|
var telemetry = new TelemetryCacheInRedis_1.TelemetryCacheInRedis(log, keys, redisClient);
|
|
@@ -46,7 +46,7 @@ var ImpressionCountsCachePluggable = /** @class */ (function (_super) {
|
|
|
46
46
|
return keys.length ? Promise.all(keys.map(function (key) { return _this.wrapper.get(key); }))
|
|
47
47
|
.then(function (counts) {
|
|
48
48
|
keys.forEach(function (key) { return _this.wrapper.del(key).catch(function () { }); });
|
|
49
|
-
var
|
|
49
|
+
var pf = [];
|
|
50
50
|
for (var i = 0; i < keys.length; i++) {
|
|
51
51
|
var key = keys[i];
|
|
52
52
|
var count = counts[i];
|
|
@@ -66,13 +66,13 @@ var ImpressionCountsCachePluggable = /** @class */ (function (_super) {
|
|
|
66
66
|
_this.log.error(constants_2.LOG_PREFIX + "Error parsing raw count " + count);
|
|
67
67
|
continue;
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
pf.push({
|
|
70
70
|
f: keyFeatureNameAndTime[1],
|
|
71
71
|
m: timeFrame,
|
|
72
72
|
rc: rawCount,
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
-
return
|
|
75
|
+
return { pf: pf };
|
|
76
76
|
}) : undefined;
|
|
77
77
|
});
|
|
78
78
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ImpressionsCachePluggable = void 0;
|
|
4
|
+
var utils_1 = require("../utils");
|
|
4
5
|
var ImpressionsCachePluggable = /** @class */ (function () {
|
|
5
6
|
function ImpressionsCachePluggable(log, key, wrapper, metadata) {
|
|
6
7
|
this.log = log;
|
|
@@ -15,25 +16,7 @@ var ImpressionsCachePluggable = /** @class */ (function () {
|
|
|
15
16
|
* or rejected if the wrapper operation fails.
|
|
16
17
|
*/
|
|
17
18
|
ImpressionsCachePluggable.prototype.track = function (impressions) {
|
|
18
|
-
return this.wrapper.pushItems(this.key,
|
|
19
|
-
};
|
|
20
|
-
ImpressionsCachePluggable.prototype._toJSON = function (impressions) {
|
|
21
|
-
var _this = this;
|
|
22
|
-
return impressions.map(function (impression) {
|
|
23
|
-
var keyName = impression.keyName, bucketingKey = impression.bucketingKey, feature = impression.feature, treatment = impression.treatment, label = impression.label, time = impression.time, changeNumber = impression.changeNumber;
|
|
24
|
-
return JSON.stringify({
|
|
25
|
-
m: _this.metadata,
|
|
26
|
-
i: {
|
|
27
|
-
k: keyName,
|
|
28
|
-
b: bucketingKey,
|
|
29
|
-
f: feature,
|
|
30
|
-
t: treatment,
|
|
31
|
-
r: label,
|
|
32
|
-
c: changeNumber,
|
|
33
|
-
m: time
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
});
|
|
19
|
+
return this.wrapper.pushItems(this.key, (0, utils_1.impressionsToJSON)(impressions, this.metadata));
|
|
37
20
|
};
|
|
38
21
|
/**
|
|
39
22
|
* Returns a promise that resolves with the count of stored impressions, or 0 if there was some error.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TelemetryCachePluggable = void 0;
|
|
4
|
-
var KeyBuilderSS_1 = require("../KeyBuilderSS");
|
|
5
4
|
var findLatencyIndex_1 = require("../findLatencyIndex");
|
|
6
5
|
var telemetrySubmitter_1 = require("../../sync/submitters/telemetrySubmitter");
|
|
7
6
|
var constants_1 = require("../../utils/constants");
|
|
8
7
|
var lang_1 = require("../../utils/lang");
|
|
9
8
|
var maps_1 = require("../../utils/lang/maps");
|
|
10
9
|
var TelemetryCacheInMemory_1 = require("../inMemory/TelemetryCacheInMemory");
|
|
10
|
+
var utils_1 = require("../utils");
|
|
11
11
|
var TelemetryCachePluggable = /** @class */ (function () {
|
|
12
12
|
/**
|
|
13
13
|
* Create a Telemetry cache that uses a storage wrapper.
|
|
@@ -44,7 +44,7 @@ var TelemetryCachePluggable = /** @class */ (function () {
|
|
|
44
44
|
var result = new maps_1._Map();
|
|
45
45
|
for (var i = 0; i < latencyKeys.length; i++) {
|
|
46
46
|
var field = latencyKeys[i].split('::')[1];
|
|
47
|
-
var parsedField = (0,
|
|
47
|
+
var parsedField = (0, utils_1.parseLatencyField)(field);
|
|
48
48
|
if ((0, lang_1.isString)(parsedField)) {
|
|
49
49
|
_this.log.error("Ignoring invalid latency field: " + field + ": " + parsedField);
|
|
50
50
|
continue;
|
|
@@ -88,7 +88,7 @@ var TelemetryCachePluggable = /** @class */ (function () {
|
|
|
88
88
|
var result = new maps_1._Map();
|
|
89
89
|
for (var i = 0; i < exceptionKeys.length; i++) {
|
|
90
90
|
var field = exceptionKeys[i].split('::')[1];
|
|
91
|
-
var parsedField = (0,
|
|
91
|
+
var parsedField = (0, utils_1.parseExceptionField)(field);
|
|
92
92
|
if ((0, lang_1.isString)(parsedField)) {
|
|
93
93
|
_this.log.error("Ignoring invalid exception field: " + field + ": " + parsedField);
|
|
94
94
|
continue;
|
|
@@ -128,7 +128,7 @@ var TelemetryCachePluggable = /** @class */ (function () {
|
|
|
128
128
|
var result = new maps_1._Map();
|
|
129
129
|
for (var i = 0; i < configKeys.length; i++) {
|
|
130
130
|
var field = configKeys[i].split('::')[1];
|
|
131
|
-
var parsedField = (0,
|
|
131
|
+
var parsedField = (0, utils_1.parseMetadata)(field);
|
|
132
132
|
if ((0, lang_1.isString)(parsedField)) {
|
|
133
133
|
_this.log.error("Ignoring invalid config field: " + field + ": " + parsedField);
|
|
134
134
|
continue;
|
|
@@ -37,29 +37,31 @@ function inMemoryWrapperFactory(connDelay) {
|
|
|
37
37
|
getKeysByPrefix: function (prefix) {
|
|
38
38
|
return Promise.resolve(Object.keys(_cache).filter(function (key) { return (0, lang_1.startsWith)(key, prefix); }));
|
|
39
39
|
},
|
|
40
|
-
incr: function (key) {
|
|
40
|
+
incr: function (key, increment) {
|
|
41
|
+
if (increment === void 0) { increment = 1; }
|
|
41
42
|
if (key in _cache) {
|
|
42
|
-
var count = (0, lang_1.toNumber)(_cache[key]) +
|
|
43
|
+
var count = (0, lang_1.toNumber)(_cache[key]) + increment;
|
|
43
44
|
if (isNaN(count))
|
|
44
45
|
return Promise.reject('Given key is not a number');
|
|
45
46
|
_cache[key] = count + '';
|
|
46
47
|
return Promise.resolve(count);
|
|
47
48
|
}
|
|
48
49
|
else {
|
|
49
|
-
_cache[key] = '
|
|
50
|
+
_cache[key] = '' + increment;
|
|
50
51
|
return Promise.resolve(1);
|
|
51
52
|
}
|
|
52
53
|
},
|
|
53
|
-
decr: function (key) {
|
|
54
|
+
decr: function (key, decrement) {
|
|
55
|
+
if (decrement === void 0) { decrement = 1; }
|
|
54
56
|
if (key in _cache) {
|
|
55
|
-
var count = (0, lang_1.toNumber)(_cache[key]) -
|
|
57
|
+
var count = (0, lang_1.toNumber)(_cache[key]) - decrement;
|
|
56
58
|
if (isNaN(count))
|
|
57
59
|
return Promise.reject('Given key is not a number');
|
|
58
60
|
_cache[key] = count + '';
|
|
59
61
|
return Promise.resolve(count);
|
|
60
62
|
}
|
|
61
63
|
else {
|
|
62
|
-
_cache[key] = '-
|
|
64
|
+
_cache[key] = '-' + decrement;
|
|
63
65
|
return Promise.resolve(-1);
|
|
64
66
|
}
|
|
65
67
|
},
|
|
@@ -20,7 +20,7 @@ var ImpressionCountsCachePluggable_1 = require("./ImpressionCountsCachePluggable
|
|
|
20
20
|
var UniqueKeysCachePluggable_1 = require("./UniqueKeysCachePluggable");
|
|
21
21
|
var UniqueKeysCacheInMemory_1 = require("../inMemory/UniqueKeysCacheInMemory");
|
|
22
22
|
var UniqueKeysCacheInMemoryCS_1 = require("../inMemory/UniqueKeysCacheInMemoryCS");
|
|
23
|
-
var
|
|
23
|
+
var utils_1 = require("../utils");
|
|
24
24
|
var NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
|
|
25
25
|
var NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
|
|
26
26
|
/**
|
|
@@ -54,7 +54,7 @@ function PluggableStorage(options) {
|
|
|
54
54
|
var prefix = (0, KeyBuilder_1.validatePrefix)(options.prefix);
|
|
55
55
|
function PluggableStorageFactory(params) {
|
|
56
56
|
var onReadyCb = params.onReadyCb, settings = params.settings, _a = params.settings, log = _a.log, mode = _a.mode, impressionsMode = _a.sync.impressionsMode, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize;
|
|
57
|
-
var metadata = (0,
|
|
57
|
+
var metadata = (0, utils_1.metadataBuilder)(settings);
|
|
58
58
|
var keys = new KeyBuilderSS_1.KeyBuilderSS(prefix, metadata);
|
|
59
59
|
var wrapper = (0, wrapperAdapter_1.wrapperAdapter)(log, options.wrapper);
|
|
60
60
|
var isSyncronizer = mode === undefined; // If mode is not defined, the synchronizer is running
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Shared utils for Redis and Pluggable storage
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.parseLatencyField = exports.parseExceptionField = exports.parseMetadata = exports.impressionsToJSON = exports.metadataBuilder = void 0;
|
|
5
|
+
var constants_1 = require("../utils/constants");
|
|
6
|
+
var TelemetryCacheInMemory_1 = require("./inMemory/TelemetryCacheInMemory");
|
|
7
|
+
var KeyBuilderSS_1 = require("./KeyBuilderSS");
|
|
8
|
+
function metadataBuilder(settings) {
|
|
9
|
+
return {
|
|
10
|
+
s: settings.version,
|
|
11
|
+
i: settings.runtime.ip || constants_1.UNKNOWN,
|
|
12
|
+
n: settings.runtime.hostname || constants_1.UNKNOWN,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
exports.metadataBuilder = metadataBuilder;
|
|
16
|
+
// Converts impressions to be stored in Redis or pluggable storage.
|
|
17
|
+
function impressionsToJSON(impressions, metadata) {
|
|
18
|
+
return impressions.map(function (impression) {
|
|
19
|
+
var impressionWithMetadata = {
|
|
20
|
+
m: metadata,
|
|
21
|
+
i: {
|
|
22
|
+
k: impression.keyName,
|
|
23
|
+
b: impression.bucketingKey,
|
|
24
|
+
f: impression.feature,
|
|
25
|
+
t: impression.treatment,
|
|
26
|
+
r: impression.label,
|
|
27
|
+
c: impression.changeNumber,
|
|
28
|
+
m: impression.time,
|
|
29
|
+
pt: impression.pt,
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return JSON.stringify(impressionWithMetadata);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
exports.impressionsToJSON = impressionsToJSON;
|
|
36
|
+
// Utilities used by TelemetryCacheInRedis and TelemetryCachePluggable
|
|
37
|
+
var REVERSE_METHOD_NAMES = Object.keys(KeyBuilderSS_1.METHOD_NAMES).reduce(function (acc, key) {
|
|
38
|
+
acc[KeyBuilderSS_1.METHOD_NAMES[key]] = key;
|
|
39
|
+
return acc;
|
|
40
|
+
}, {});
|
|
41
|
+
function parseMetadata(field) {
|
|
42
|
+
var parts = field.split('/');
|
|
43
|
+
if (parts.length !== 3)
|
|
44
|
+
return "invalid subsection count. Expected 3, got: " + parts.length;
|
|
45
|
+
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */;
|
|
46
|
+
return [JSON.stringify({ s: s, n: n, i: i })];
|
|
47
|
+
}
|
|
48
|
+
exports.parseMetadata = parseMetadata;
|
|
49
|
+
function parseExceptionField(field) {
|
|
50
|
+
var parts = field.split('/');
|
|
51
|
+
if (parts.length !== 4)
|
|
52
|
+
return "invalid subsection count. Expected 4, got: " + parts.length;
|
|
53
|
+
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */, m = parts[3];
|
|
54
|
+
var method = REVERSE_METHOD_NAMES[m];
|
|
55
|
+
if (!method)
|
|
56
|
+
return "unknown method '" + m + "'";
|
|
57
|
+
return [JSON.stringify({ s: s, n: n, i: i }), method];
|
|
58
|
+
}
|
|
59
|
+
exports.parseExceptionField = parseExceptionField;
|
|
60
|
+
function parseLatencyField(field) {
|
|
61
|
+
var parts = field.split('/');
|
|
62
|
+
if (parts.length !== 5)
|
|
63
|
+
return "invalid subsection count. Expected 5, got: " + parts.length;
|
|
64
|
+
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */, m = parts[3], b = parts[4];
|
|
65
|
+
var method = REVERSE_METHOD_NAMES[m];
|
|
66
|
+
if (!method)
|
|
67
|
+
return "unknown method '" + m + "'";
|
|
68
|
+
var bucket = parseInt(b);
|
|
69
|
+
if (isNaN(bucket) || bucket >= TelemetryCacheInMemory_1.MAX_LATENCY_BUCKET_COUNT)
|
|
70
|
+
return "invalid bucket. Expected a number between 0 and " + (TelemetryCacheInMemory_1.MAX_LATENCY_BUCKET_COUNT - 1) + ", got: " + b;
|
|
71
|
+
return [JSON.stringify({ s: s, n: n, i: i }), method, bucket];
|
|
72
|
+
}
|
|
73
|
+
exports.parseLatencyField = parseLatencyField;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { __extends } from "tslib";
|
|
2
2
|
import { KeyBuilder } from './KeyBuilder';
|
|
3
|
-
|
|
4
|
-
var METHOD_NAMES = {
|
|
3
|
+
export var METHOD_NAMES = {
|
|
5
4
|
t: 'treatment',
|
|
6
5
|
ts: 'treatments',
|
|
7
6
|
tc: 'treatmentWithConfig',
|
|
@@ -49,38 +48,3 @@ var KeyBuilderSS = /** @class */ (function (_super) {
|
|
|
49
48
|
return KeyBuilderSS;
|
|
50
49
|
}(KeyBuilder));
|
|
51
50
|
export { KeyBuilderSS };
|
|
52
|
-
// Used by consumer methods of TelemetryCacheInRedis and TelemetryCachePluggable
|
|
53
|
-
var REVERSE_METHOD_NAMES = Object.keys(METHOD_NAMES).reduce(function (acc, key) {
|
|
54
|
-
acc[METHOD_NAMES[key]] = key;
|
|
55
|
-
return acc;
|
|
56
|
-
}, {});
|
|
57
|
-
export function parseMetadata(field) {
|
|
58
|
-
var parts = field.split('/');
|
|
59
|
-
if (parts.length !== 3)
|
|
60
|
-
return "invalid subsection count. Expected 3, got: " + parts.length;
|
|
61
|
-
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */;
|
|
62
|
-
return [JSON.stringify({ s: s, n: n, i: i })];
|
|
63
|
-
}
|
|
64
|
-
export function parseExceptionField(field) {
|
|
65
|
-
var parts = field.split('/');
|
|
66
|
-
if (parts.length !== 4)
|
|
67
|
-
return "invalid subsection count. Expected 4, got: " + parts.length;
|
|
68
|
-
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */, m = parts[3];
|
|
69
|
-
var method = REVERSE_METHOD_NAMES[m];
|
|
70
|
-
if (!method)
|
|
71
|
-
return "unknown method '" + m + "'";
|
|
72
|
-
return [JSON.stringify({ s: s, n: n, i: i }), method];
|
|
73
|
-
}
|
|
74
|
-
export function parseLatencyField(field) {
|
|
75
|
-
var parts = field.split('/');
|
|
76
|
-
if (parts.length !== 5)
|
|
77
|
-
return "invalid subsection count. Expected 5, got: " + parts.length;
|
|
78
|
-
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */, m = parts[3], b = parts[4];
|
|
79
|
-
var method = REVERSE_METHOD_NAMES[m];
|
|
80
|
-
if (!method)
|
|
81
|
-
return "unknown method '" + m + "'";
|
|
82
|
-
var bucket = parseInt(b);
|
|
83
|
-
if (isNaN(bucket) || bucket >= MAX_LATENCY_BUCKET_COUNT)
|
|
84
|
-
return "invalid bucket. Expected a number between 0 and " + (MAX_LATENCY_BUCKET_COUNT - 1) + ", got: " + b;
|
|
85
|
-
return [JSON.stringify({ s: s, n: n, i: i }), method, bucket];
|
|
86
|
-
}
|
|
@@ -51,7 +51,7 @@ var ImpressionCountsCacheInRedis = /** @class */ (function (_super) {
|
|
|
51
51
|
if (!Object.keys(counts).length)
|
|
52
52
|
return undefined;
|
|
53
53
|
_this.redis.del(_this.key).catch(function () { });
|
|
54
|
-
var
|
|
54
|
+
var pf = [];
|
|
55
55
|
forOwn(counts, function (count, key) {
|
|
56
56
|
var nameAndTime = key.split('::');
|
|
57
57
|
if (nameAndTime.length !== 2) {
|
|
@@ -68,13 +68,13 @@ var ImpressionCountsCacheInRedis = /** @class */ (function (_super) {
|
|
|
68
68
|
_this.log.error(LOG_PREFIX + "Error parsing raw count " + count);
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
pf.push({
|
|
72
72
|
f: nameAndTime[0],
|
|
73
73
|
m: timeFrame,
|
|
74
74
|
rc: rawCount,
|
|
75
75
|
});
|
|
76
76
|
});
|
|
77
|
-
return
|
|
77
|
+
return { pf: pf };
|
|
78
78
|
});
|
|
79
79
|
};
|
|
80
80
|
return ImpressionCountsCacheInRedis;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { impressionsToJSON } from '../utils';
|
|
1
2
|
var IMPRESSIONS_TTL_REFRESH = 3600; // 1 hr
|
|
2
3
|
var ImpressionsCacheInRedis = /** @class */ (function () {
|
|
3
4
|
function ImpressionsCacheInRedis(log, key, redis, metadata) {
|
|
@@ -8,31 +9,13 @@ var ImpressionsCacheInRedis = /** @class */ (function () {
|
|
|
8
9
|
}
|
|
9
10
|
ImpressionsCacheInRedis.prototype.track = function (impressions) {
|
|
10
11
|
var _this = this;
|
|
11
|
-
return this.redis.rpush(this.key, this.
|
|
12
|
+
return this.redis.rpush(this.key, impressionsToJSON(impressions, this.metadata)).then(function (queuedCount) {
|
|
12
13
|
// If this is the creation of the key on Redis, set the expiration for it in 1hr.
|
|
13
14
|
if (queuedCount === impressions.length) {
|
|
14
15
|
return _this.redis.expire(_this.key, IMPRESSIONS_TTL_REFRESH);
|
|
15
16
|
}
|
|
16
17
|
});
|
|
17
18
|
};
|
|
18
|
-
ImpressionsCacheInRedis.prototype._toJSON = function (impressions) {
|
|
19
|
-
var _this = this;
|
|
20
|
-
return impressions.map(function (impression) {
|
|
21
|
-
var keyName = impression.keyName, bucketingKey = impression.bucketingKey, feature = impression.feature, treatment = impression.treatment, label = impression.label, time = impression.time, changeNumber = impression.changeNumber;
|
|
22
|
-
return JSON.stringify({
|
|
23
|
-
m: _this.metadata,
|
|
24
|
-
i: {
|
|
25
|
-
k: keyName,
|
|
26
|
-
b: bucketingKey,
|
|
27
|
-
f: feature,
|
|
28
|
-
t: treatment,
|
|
29
|
-
r: label,
|
|
30
|
-
c: changeNumber,
|
|
31
|
-
m: time
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
};
|
|
36
19
|
ImpressionsCacheInRedis.prototype.count = function () {
|
|
37
20
|
return this.redis.llen(this.key).catch(function () { return 0; });
|
|
38
21
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { parseExceptionField, parseLatencyField, parseMetadata } from '../KeyBuilderSS';
|
|
2
1
|
import { findLatencyIndex } from '../findLatencyIndex';
|
|
3
2
|
import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter';
|
|
4
3
|
import { CONSUMER_MODE, STORAGE_REDIS } from '../../utils/constants';
|
|
5
4
|
import { isNaNNumber, isString } from '../../utils/lang';
|
|
6
5
|
import { _Map } from '../../utils/lang/maps';
|
|
7
6
|
import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory';
|
|
7
|
+
import { parseLatencyField, parseExceptionField, parseMetadata } from '../utils';
|
|
8
8
|
var TelemetryCacheInRedis = /** @class */ (function () {
|
|
9
9
|
/**
|
|
10
10
|
* Create a Telemetry cache that uses Redis as storage.
|
|
@@ -9,7 +9,7 @@ import { DEBUG, NONE, STORAGE_REDIS } from '../../utils/constants';
|
|
|
9
9
|
import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
|
|
10
10
|
import { UniqueKeysCacheInRedis } from './UniqueKeysCacheInRedis';
|
|
11
11
|
import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
|
|
12
|
-
import { metadataBuilder } from '../
|
|
12
|
+
import { metadataBuilder } from '../utils';
|
|
13
13
|
/**
|
|
14
14
|
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.
|
|
15
15
|
* @see {@link https://www.npmjs.com/package/ioredis}
|
|
@@ -43,7 +43,7 @@ var ImpressionCountsCachePluggable = /** @class */ (function (_super) {
|
|
|
43
43
|
return keys.length ? Promise.all(keys.map(function (key) { return _this.wrapper.get(key); }))
|
|
44
44
|
.then(function (counts) {
|
|
45
45
|
keys.forEach(function (key) { return _this.wrapper.del(key).catch(function () { }); });
|
|
46
|
-
var
|
|
46
|
+
var pf = [];
|
|
47
47
|
for (var i = 0; i < keys.length; i++) {
|
|
48
48
|
var key = keys[i];
|
|
49
49
|
var count = counts[i];
|
|
@@ -63,13 +63,13 @@ var ImpressionCountsCachePluggable = /** @class */ (function (_super) {
|
|
|
63
63
|
_this.log.error(LOG_PREFIX + "Error parsing raw count " + count);
|
|
64
64
|
continue;
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
pf.push({
|
|
67
67
|
f: keyFeatureNameAndTime[1],
|
|
68
68
|
m: timeFrame,
|
|
69
69
|
rc: rawCount,
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
|
-
return
|
|
72
|
+
return { pf: pf };
|
|
73
73
|
}) : undefined;
|
|
74
74
|
});
|
|
75
75
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { impressionsToJSON } from '../utils';
|
|
1
2
|
var ImpressionsCachePluggable = /** @class */ (function () {
|
|
2
3
|
function ImpressionsCachePluggable(log, key, wrapper, metadata) {
|
|
3
4
|
this.log = log;
|
|
@@ -12,25 +13,7 @@ var ImpressionsCachePluggable = /** @class */ (function () {
|
|
|
12
13
|
* or rejected if the wrapper operation fails.
|
|
13
14
|
*/
|
|
14
15
|
ImpressionsCachePluggable.prototype.track = function (impressions) {
|
|
15
|
-
return this.wrapper.pushItems(this.key, this.
|
|
16
|
-
};
|
|
17
|
-
ImpressionsCachePluggable.prototype._toJSON = function (impressions) {
|
|
18
|
-
var _this = this;
|
|
19
|
-
return impressions.map(function (impression) {
|
|
20
|
-
var keyName = impression.keyName, bucketingKey = impression.bucketingKey, feature = impression.feature, treatment = impression.treatment, label = impression.label, time = impression.time, changeNumber = impression.changeNumber;
|
|
21
|
-
return JSON.stringify({
|
|
22
|
-
m: _this.metadata,
|
|
23
|
-
i: {
|
|
24
|
-
k: keyName,
|
|
25
|
-
b: bucketingKey,
|
|
26
|
-
f: feature,
|
|
27
|
-
t: treatment,
|
|
28
|
-
r: label,
|
|
29
|
-
c: changeNumber,
|
|
30
|
-
m: time
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
});
|
|
16
|
+
return this.wrapper.pushItems(this.key, impressionsToJSON(impressions, this.metadata));
|
|
34
17
|
};
|
|
35
18
|
/**
|
|
36
19
|
* Returns a promise that resolves with the count of stored impressions, or 0 if there was some error.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { parseExceptionField, parseLatencyField, parseMetadata } from '../KeyBuilderSS';
|
|
2
1
|
import { findLatencyIndex } from '../findLatencyIndex';
|
|
3
2
|
import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter';
|
|
4
3
|
import { CONSUMER_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
|
|
5
4
|
import { isString, isNaNNumber } from '../../utils/lang';
|
|
6
5
|
import { _Map } from '../../utils/lang/maps';
|
|
7
6
|
import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory';
|
|
7
|
+
import { parseLatencyField, parseExceptionField, parseMetadata } from '../utils';
|
|
8
8
|
var TelemetryCachePluggable = /** @class */ (function () {
|
|
9
9
|
/**
|
|
10
10
|
* Create a Telemetry cache that uses a storage wrapper.
|