@splitsoftware/splitio-commons 1.6.2-rc.12 → 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/listeners/browser.js +9 -11
- package/cjs/sdkFactory/index.js +7 -24
- package/cjs/storages/KeyBuilderSS.js +4 -43
- package/cjs/storages/inLocalStorage/index.js +14 -10
- package/cjs/storages/inMemory/InMemoryStorage.js +10 -7
- package/cjs/storages/inMemory/InMemoryStorageCS.js +10 -7
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +57 -34
- 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 +4 -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 +4 -2
- package/cjs/storages/utils.js +73 -0
- package/cjs/sync/submitters/submitterManager.js +1 -1
- package/cjs/sync/submitters/telemetrySubmitter.js +4 -40
- package/cjs/trackers/impressionObserver/utils.js +1 -17
- package/cjs/trackers/uniqueKeysTracker.js +1 -1
- package/cjs/utils/settingsValidation/index.js +7 -1
- package/esm/listeners/browser.js +9 -11
- package/esm/sdkFactory/index.js +7 -24
- package/esm/storages/KeyBuilderSS.js +1 -37
- package/esm/storages/inLocalStorage/index.js +15 -11
- package/esm/storages/inMemory/InMemoryStorage.js +10 -7
- package/esm/storages/inMemory/InMemoryStorageCS.js +10 -7
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +58 -35
- 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 +4 -2
- 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 +4 -2
- package/esm/storages/utils.js +65 -0
- package/esm/sync/submitters/submitterManager.js +1 -1
- package/esm/sync/submitters/telemetrySubmitter.js +4 -39
- package/esm/trackers/impressionObserver/utils.js +1 -15
- package/esm/trackers/uniqueKeysTracker.js +1 -1
- package/esm/utils/settingsValidation/index.js +7 -1
- package/package.json +2 -1
- package/src/listeners/browser.ts +9 -13
- package/src/sdkClient/sdkClient.ts +1 -1
- package/src/sdkFactory/index.ts +7 -29
- package/src/sdkFactory/types.ts +2 -2
- package/src/services/splitApi.ts +2 -2
- package/src/storages/KeyBuilderSS.ts +2 -44
- package/src/storages/inLocalStorage/index.ts +16 -11
- package/src/storages/inMemory/AttributesCacheInMemory.ts +7 -7
- package/src/storages/inMemory/InMemoryStorage.ts +11 -7
- package/src/storages/inMemory/InMemoryStorageCS.ts +11 -7
- package/src/storages/inMemory/TelemetryCacheInMemory.ts +66 -33
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/ImpressionsCacheInRedis.ts +2 -22
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +3 -2
- package/src/storages/inRedis/index.ts +4 -1
- package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +3 -3
- package/src/storages/pluggable/ImpressionsCachePluggable.ts +3 -23
- package/src/storages/pluggable/TelemetryCachePluggable.ts +3 -2
- package/src/storages/pluggable/inMemoryWrapper.ts +6 -6
- package/src/storages/pluggable/index.ts +4 -2
- package/src/storages/types.ts +45 -64
- package/src/storages/utils.ts +78 -0
- package/src/sync/submitters/submitter.ts +2 -2
- package/src/sync/submitters/submitterManager.ts +1 -1
- package/src/sync/submitters/telemetrySubmitter.ts +5 -41
- package/src/sync/submitters/types.ts +7 -5
- package/src/trackers/impressionObserver/utils.ts +1 -16
- package/src/trackers/impressionsTracker.ts +2 -2
- package/src/trackers/strategy/strategyDebug.ts +4 -4
- package/src/trackers/strategy/strategyNone.ts +9 -9
- package/src/trackers/strategy/strategyOptimized.ts +9 -9
- package/src/trackers/uniqueKeysTracker.ts +6 -6
- package/src/utils/redis/RedisMock.ts +5 -5
- package/src/utils/settingsValidation/index.ts +5 -1
- package/types/storages/KeyBuilderSS.d.ts +1 -3
- package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +19 -8
- package/types/storages/inRedis/ImpressionsCacheInRedis.d.ts +0 -1
- package/types/storages/pluggable/ImpressionsCachePluggable.d.ts +1 -2
- package/types/storages/types.d.ts +35 -45
- package/types/storages/utils.d.ts +8 -0
- package/types/sync/submitters/submitter.d.ts +2 -2
- package/types/sync/submitters/telemetrySubmitter.d.ts +2 -10
- package/types/sync/submitters/types.d.ts +8 -6
- package/types/trackers/impressionObserver/utils.d.ts +0 -8
- package/types/trackers/strategy/strategyNone.d.ts +2 -2
- package/types/trackers/strategy/strategyOptimized.d.ts +2 -2
- package/types/trackers/uniqueKeysTracker.d.ts +1 -1
- package/cjs/storages/metadataBuilder.js +0 -12
- package/esm/storages/metadataBuilder.js +0 -8
- package/src/storages/metadataBuilder.ts +0 -11
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LOCALHOST_MODE } from '../../utils/constants';
|
|
1
|
+
import { DEDUPED, DROPPED, LOCALHOST_MODE, QUEUED } from '../../utils/constants';
|
|
2
2
|
import { findLatencyIndex } from '../findLatencyIndex';
|
|
3
3
|
var MAX_STREAMING_EVENTS = 20;
|
|
4
4
|
var MAX_TAGS = 10;
|
|
@@ -13,29 +13,55 @@ var ACCEPTANCE_RANGE = 0.001;
|
|
|
13
13
|
* Record telemetry if mode is not localhost.
|
|
14
14
|
* All factory instances track telemetry on server-side, and 0.1% on client-side.
|
|
15
15
|
*/
|
|
16
|
-
export function shouldRecordTelemetry(
|
|
17
|
-
|
|
16
|
+
export function shouldRecordTelemetry(_a) {
|
|
17
|
+
var settings = _a.settings;
|
|
18
|
+
return settings.mode !== LOCALHOST_MODE && (settings.core.key === undefined || Math.random() <= ACCEPTANCE_RANGE);
|
|
18
19
|
}
|
|
19
20
|
var TelemetryCacheInMemory = /** @class */ (function () {
|
|
20
|
-
function TelemetryCacheInMemory() {
|
|
21
|
+
function TelemetryCacheInMemory(splits, segments) {
|
|
22
|
+
this.splits = splits;
|
|
23
|
+
this.segments = segments;
|
|
24
|
+
// isEmpty flag
|
|
25
|
+
this.e = true;
|
|
21
26
|
this.notReadyUsage = 0;
|
|
27
|
+
/** Usage stats */
|
|
22
28
|
this.impressionStats = [0, 0, 0];
|
|
23
29
|
this.eventStats = [0, 0];
|
|
24
|
-
// @ts-expect-error
|
|
25
30
|
this.lastSync = {};
|
|
26
|
-
// @ts-expect-error
|
|
27
31
|
this.httpErrors = {};
|
|
28
|
-
// @ts-expect-error
|
|
29
32
|
this.httpLatencies = {};
|
|
30
33
|
this.authRejections = 0;
|
|
31
34
|
this.tokenRefreshes = 0;
|
|
32
35
|
this.streamingEvents = [];
|
|
33
36
|
this.tags = [];
|
|
34
|
-
// @ts-expect-error
|
|
35
37
|
this.exceptions = {};
|
|
36
|
-
// @ts-expect-error
|
|
37
38
|
this.latencies = {};
|
|
38
39
|
}
|
|
40
|
+
TelemetryCacheInMemory.prototype.isEmpty = function () { return this.e; };
|
|
41
|
+
TelemetryCacheInMemory.prototype.clear = function () { };
|
|
42
|
+
TelemetryCacheInMemory.prototype.pop = function () {
|
|
43
|
+
this.e = true;
|
|
44
|
+
return {
|
|
45
|
+
lS: this.getLastSynchronization(),
|
|
46
|
+
mL: this.popLatencies(),
|
|
47
|
+
mE: this.popExceptions(),
|
|
48
|
+
hE: this.popHttpErrors(),
|
|
49
|
+
hL: this.popHttpLatencies(),
|
|
50
|
+
tR: this.popTokenRefreshes(),
|
|
51
|
+
aR: this.popAuthRejections(),
|
|
52
|
+
iQ: this.getImpressionStats(QUEUED),
|
|
53
|
+
iDe: this.getImpressionStats(DEDUPED),
|
|
54
|
+
iDr: this.getImpressionStats(DROPPED),
|
|
55
|
+
spC: this.splits && this.splits.getSplitNames().length,
|
|
56
|
+
seC: this.segments && this.segments.getRegisteredSegments().length,
|
|
57
|
+
skC: this.segments && this.segments.getKeysCount(),
|
|
58
|
+
sL: this.getSessionLength(),
|
|
59
|
+
eQ: this.getEventStats(QUEUED),
|
|
60
|
+
eD: this.getEventStats(DROPPED),
|
|
61
|
+
sE: this.popStreamingEvents(),
|
|
62
|
+
t: this.popTags(),
|
|
63
|
+
};
|
|
64
|
+
};
|
|
39
65
|
TelemetryCacheInMemory.prototype.getTimeUntilReady = function () {
|
|
40
66
|
return this.timeUntilReady;
|
|
41
67
|
};
|
|
@@ -59,44 +85,41 @@ var TelemetryCacheInMemory = /** @class */ (function () {
|
|
|
59
85
|
};
|
|
60
86
|
TelemetryCacheInMemory.prototype.recordImpressionStats = function (type, count) {
|
|
61
87
|
this.impressionStats[type] += count;
|
|
88
|
+
this.e = false;
|
|
62
89
|
};
|
|
63
90
|
TelemetryCacheInMemory.prototype.getEventStats = function (type) {
|
|
64
91
|
return this.eventStats[type];
|
|
65
92
|
};
|
|
66
93
|
TelemetryCacheInMemory.prototype.recordEventStats = function (type, count) {
|
|
67
94
|
this.eventStats[type] += count;
|
|
95
|
+
this.e = false;
|
|
68
96
|
};
|
|
69
97
|
TelemetryCacheInMemory.prototype.getLastSynchronization = function () {
|
|
70
98
|
return this.lastSync;
|
|
71
99
|
};
|
|
72
100
|
TelemetryCacheInMemory.prototype.recordSuccessfulSync = function (resource, timeMs) {
|
|
73
101
|
this.lastSync[resource] = timeMs;
|
|
102
|
+
this.e = false;
|
|
74
103
|
};
|
|
75
104
|
TelemetryCacheInMemory.prototype.popHttpErrors = function () {
|
|
76
|
-
var result = this.httpErrors;
|
|
105
|
+
var result = this.httpErrors;
|
|
77
106
|
this.httpErrors = {};
|
|
78
107
|
return result;
|
|
79
108
|
};
|
|
80
109
|
TelemetryCacheInMemory.prototype.recordHttpError = function (resource, status) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
this.httpErrors[resource][status] = 1;
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
this.httpErrors[resource][status]++;
|
|
88
|
-
}
|
|
110
|
+
var statusErrors = (this.httpErrors[resource] = this.httpErrors[resource] || {});
|
|
111
|
+
statusErrors[status] = (statusErrors[status] || 0) + 1;
|
|
112
|
+
this.e = false;
|
|
89
113
|
};
|
|
90
114
|
TelemetryCacheInMemory.prototype.popHttpLatencies = function () {
|
|
91
|
-
var result = this.httpLatencies;
|
|
115
|
+
var result = this.httpLatencies;
|
|
92
116
|
this.httpLatencies = {};
|
|
93
117
|
return result;
|
|
94
118
|
};
|
|
95
119
|
TelemetryCacheInMemory.prototype.recordHttpLatency = function (resource, latencyMs) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
this.httpLatencies[resource][findLatencyIndex(latencyMs)]++;
|
|
120
|
+
var latencyBuckets = (this.httpLatencies[resource] = this.httpLatencies[resource] || newBuckets());
|
|
121
|
+
latencyBuckets[findLatencyIndex(latencyMs)]++;
|
|
122
|
+
this.e = false;
|
|
100
123
|
};
|
|
101
124
|
TelemetryCacheInMemory.prototype.popAuthRejections = function () {
|
|
102
125
|
var result = this.authRejections;
|
|
@@ -105,6 +128,7 @@ var TelemetryCacheInMemory = /** @class */ (function () {
|
|
|
105
128
|
};
|
|
106
129
|
TelemetryCacheInMemory.prototype.recordAuthRejections = function () {
|
|
107
130
|
this.authRejections++;
|
|
131
|
+
this.e = false;
|
|
108
132
|
};
|
|
109
133
|
TelemetryCacheInMemory.prototype.popTokenRefreshes = function () {
|
|
110
134
|
var result = this.tokenRefreshes;
|
|
@@ -113,6 +137,7 @@ var TelemetryCacheInMemory = /** @class */ (function () {
|
|
|
113
137
|
};
|
|
114
138
|
TelemetryCacheInMemory.prototype.recordTokenRefreshes = function () {
|
|
115
139
|
this.tokenRefreshes++;
|
|
140
|
+
this.e = false;
|
|
116
141
|
};
|
|
117
142
|
TelemetryCacheInMemory.prototype.popStreamingEvents = function () {
|
|
118
143
|
return this.streamingEvents.splice(0);
|
|
@@ -121,6 +146,7 @@ var TelemetryCacheInMemory = /** @class */ (function () {
|
|
|
121
146
|
if (this.streamingEvents.length < MAX_STREAMING_EVENTS) {
|
|
122
147
|
this.streamingEvents.push(streamingEvent);
|
|
123
148
|
}
|
|
149
|
+
this.e = false;
|
|
124
150
|
};
|
|
125
151
|
TelemetryCacheInMemory.prototype.popTags = function () {
|
|
126
152
|
return this.tags.splice(0);
|
|
@@ -129,36 +155,33 @@ var TelemetryCacheInMemory = /** @class */ (function () {
|
|
|
129
155
|
if (this.tags.length < MAX_TAGS) {
|
|
130
156
|
this.tags.push(tag);
|
|
131
157
|
}
|
|
158
|
+
this.e = false;
|
|
132
159
|
};
|
|
133
160
|
TelemetryCacheInMemory.prototype.getSessionLength = function () {
|
|
134
161
|
return this.sessionLength;
|
|
135
162
|
};
|
|
136
163
|
TelemetryCacheInMemory.prototype.recordSessionLength = function (ms) {
|
|
137
164
|
this.sessionLength = ms;
|
|
165
|
+
this.e = false;
|
|
138
166
|
};
|
|
139
167
|
TelemetryCacheInMemory.prototype.popExceptions = function () {
|
|
140
|
-
var result = this.exceptions;
|
|
168
|
+
var result = this.exceptions;
|
|
141
169
|
this.exceptions = {};
|
|
142
170
|
return result;
|
|
143
171
|
};
|
|
144
172
|
TelemetryCacheInMemory.prototype.recordException = function (method) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
this.exceptions[method]++;
|
|
150
|
-
}
|
|
173
|
+
this.exceptions[method] = (this.exceptions[method] || 0) + 1;
|
|
174
|
+
this.e = false;
|
|
151
175
|
};
|
|
152
176
|
TelemetryCacheInMemory.prototype.popLatencies = function () {
|
|
153
|
-
var result = this.latencies;
|
|
177
|
+
var result = this.latencies;
|
|
154
178
|
this.latencies = {};
|
|
155
179
|
return result;
|
|
156
180
|
};
|
|
157
181
|
TelemetryCacheInMemory.prototype.recordLatency = function (method, latencyMs) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
this.latencies[method][findLatencyIndex(latencyMs)]++;
|
|
182
|
+
var latencyBuckets = (this.latencies[method] = this.latencies[method] || newBuckets());
|
|
183
|
+
latencyBuckets[findLatencyIndex(latencyMs)]++;
|
|
184
|
+
this.e = false;
|
|
162
185
|
};
|
|
163
186
|
return TelemetryCacheInMemory;
|
|
164
187
|
}());
|
|
@@ -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,6 +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 '../utils';
|
|
12
13
|
/**
|
|
13
14
|
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.
|
|
14
15
|
* @see {@link https://www.npmjs.com/package/ioredis}
|
|
@@ -16,8 +17,9 @@ import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
|
|
|
16
17
|
export function InRedisStorage(options) {
|
|
17
18
|
if (options === void 0) { options = {}; }
|
|
18
19
|
var prefix = validatePrefix(options.prefix);
|
|
19
|
-
function InRedisStorageFactory(
|
|
20
|
-
var
|
|
20
|
+
function InRedisStorageFactory(params) {
|
|
21
|
+
var onReadyCb = params.onReadyCb, settings = params.settings, _a = params.settings, log = _a.log, impressionsMode = _a.sync.impressionsMode;
|
|
22
|
+
var metadata = metadataBuilder(settings);
|
|
21
23
|
var keys = new KeyBuilderSS(prefix, metadata);
|
|
22
24
|
var redisClient = new RedisAdapter(log, options.options || {});
|
|
23
25
|
var telemetry = new TelemetryCacheInRedis(log, keys, redisClient);
|
|
@@ -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.
|
|
@@ -34,29 +34,31 @@ export function inMemoryWrapperFactory(connDelay) {
|
|
|
34
34
|
getKeysByPrefix: function (prefix) {
|
|
35
35
|
return Promise.resolve(Object.keys(_cache).filter(function (key) { return startsWith(key, prefix); }));
|
|
36
36
|
},
|
|
37
|
-
incr: function (key) {
|
|
37
|
+
incr: function (key, increment) {
|
|
38
|
+
if (increment === void 0) { increment = 1; }
|
|
38
39
|
if (key in _cache) {
|
|
39
|
-
var count = toNumber(_cache[key]) +
|
|
40
|
+
var count = toNumber(_cache[key]) + increment;
|
|
40
41
|
if (isNaN(count))
|
|
41
42
|
return Promise.reject('Given key is not a number');
|
|
42
43
|
_cache[key] = count + '';
|
|
43
44
|
return Promise.resolve(count);
|
|
44
45
|
}
|
|
45
46
|
else {
|
|
46
|
-
_cache[key] = '
|
|
47
|
+
_cache[key] = '' + increment;
|
|
47
48
|
return Promise.resolve(1);
|
|
48
49
|
}
|
|
49
50
|
},
|
|
50
|
-
decr: function (key) {
|
|
51
|
+
decr: function (key, decrement) {
|
|
52
|
+
if (decrement === void 0) { decrement = 1; }
|
|
51
53
|
if (key in _cache) {
|
|
52
|
-
var count = toNumber(_cache[key]) -
|
|
54
|
+
var count = toNumber(_cache[key]) - decrement;
|
|
53
55
|
if (isNaN(count))
|
|
54
56
|
return Promise.reject('Given key is not a number');
|
|
55
57
|
_cache[key] = count + '';
|
|
56
58
|
return Promise.resolve(count);
|
|
57
59
|
}
|
|
58
60
|
else {
|
|
59
|
-
_cache[key] = '-
|
|
61
|
+
_cache[key] = '-' + decrement;
|
|
60
62
|
return Promise.resolve(-1);
|
|
61
63
|
}
|
|
62
64
|
},
|
|
@@ -17,6 +17,7 @@ import { ImpressionCountsCachePluggable } from './ImpressionCountsCachePluggable
|
|
|
17
17
|
import { UniqueKeysCachePluggable } from './UniqueKeysCachePluggable';
|
|
18
18
|
import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
|
|
19
19
|
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
|
|
20
|
+
import { metadataBuilder } from '../utils';
|
|
20
21
|
var NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
|
|
21
22
|
var NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
|
|
22
23
|
/**
|
|
@@ -49,7 +50,8 @@ export function PluggableStorage(options) {
|
|
|
49
50
|
validatePluggableStorageOptions(options);
|
|
50
51
|
var prefix = validatePrefix(options.prefix);
|
|
51
52
|
function PluggableStorageFactory(params) {
|
|
52
|
-
var
|
|
53
|
+
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;
|
|
54
|
+
var metadata = metadataBuilder(settings);
|
|
53
55
|
var keys = new KeyBuilderSS(prefix, metadata);
|
|
54
56
|
var wrapper = wrapperAdapter(log, options.wrapper);
|
|
55
57
|
var isSyncronizer = mode === undefined; // If mode is not defined, the synchronizer is running
|
|
@@ -66,7 +68,7 @@ export function PluggableStorage(options) {
|
|
|
66
68
|
undefined;
|
|
67
69
|
var uniqueKeysCache = impressionsMode === NONE || isSyncronizer ?
|
|
68
70
|
isPartialConsumer ?
|
|
69
|
-
|
|
71
|
+
settings.core.key === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
|
|
70
72
|
new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
|
|
71
73
|
undefined;
|
|
72
74
|
// Connects to wrapper and emits SDK_READY event on main client
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Shared utils for Redis and Pluggable storage
|
|
2
|
+
import { UNKNOWN } from '../utils/constants';
|
|
3
|
+
import { MAX_LATENCY_BUCKET_COUNT } from './inMemory/TelemetryCacheInMemory';
|
|
4
|
+
import { METHOD_NAMES } from './KeyBuilderSS';
|
|
5
|
+
export function metadataBuilder(settings) {
|
|
6
|
+
return {
|
|
7
|
+
s: settings.version,
|
|
8
|
+
i: settings.runtime.ip || UNKNOWN,
|
|
9
|
+
n: settings.runtime.hostname || UNKNOWN,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
// Converts impressions to be stored in Redis or pluggable storage.
|
|
13
|
+
export function impressionsToJSON(impressions, metadata) {
|
|
14
|
+
return impressions.map(function (impression) {
|
|
15
|
+
var impressionWithMetadata = {
|
|
16
|
+
m: metadata,
|
|
17
|
+
i: {
|
|
18
|
+
k: impression.keyName,
|
|
19
|
+
b: impression.bucketingKey,
|
|
20
|
+
f: impression.feature,
|
|
21
|
+
t: impression.treatment,
|
|
22
|
+
r: impression.label,
|
|
23
|
+
c: impression.changeNumber,
|
|
24
|
+
m: impression.time,
|
|
25
|
+
pt: impression.pt,
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
return JSON.stringify(impressionWithMetadata);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// Utilities used by TelemetryCacheInRedis and TelemetryCachePluggable
|
|
32
|
+
var REVERSE_METHOD_NAMES = Object.keys(METHOD_NAMES).reduce(function (acc, key) {
|
|
33
|
+
acc[METHOD_NAMES[key]] = key;
|
|
34
|
+
return acc;
|
|
35
|
+
}, {});
|
|
36
|
+
export function parseMetadata(field) {
|
|
37
|
+
var parts = field.split('/');
|
|
38
|
+
if (parts.length !== 3)
|
|
39
|
+
return "invalid subsection count. Expected 3, got: " + parts.length;
|
|
40
|
+
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */;
|
|
41
|
+
return [JSON.stringify({ s: s, n: n, i: i })];
|
|
42
|
+
}
|
|
43
|
+
export function parseExceptionField(field) {
|
|
44
|
+
var parts = field.split('/');
|
|
45
|
+
if (parts.length !== 4)
|
|
46
|
+
return "invalid subsection count. Expected 4, got: " + parts.length;
|
|
47
|
+
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */, m = parts[3];
|
|
48
|
+
var method = REVERSE_METHOD_NAMES[m];
|
|
49
|
+
if (!method)
|
|
50
|
+
return "unknown method '" + m + "'";
|
|
51
|
+
return [JSON.stringify({ s: s, n: n, i: i }), method];
|
|
52
|
+
}
|
|
53
|
+
export function parseLatencyField(field) {
|
|
54
|
+
var parts = field.split('/');
|
|
55
|
+
if (parts.length !== 5)
|
|
56
|
+
return "invalid subsection count. Expected 5, got: " + parts.length;
|
|
57
|
+
var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */, m = parts[3], b = parts[4];
|
|
58
|
+
var method = REVERSE_METHOD_NAMES[m];
|
|
59
|
+
if (!method)
|
|
60
|
+
return "unknown method '" + m + "'";
|
|
61
|
+
var bucket = parseInt(b);
|
|
62
|
+
if (isNaN(bucket) || bucket >= MAX_LATENCY_BUCKET_COUNT)
|
|
63
|
+
return "invalid bucket. Expected a number between 0 and " + (MAX_LATENCY_BUCKET_COUNT - 1) + ", got: " + b;
|
|
64
|
+
return [JSON.stringify({ s: s, n: n, i: i }), method, bucket];
|
|
65
|
+
}
|
|
@@ -12,7 +12,7 @@ export function submitterManagerFactory(params) {
|
|
|
12
12
|
if (impressionCountsSubmitter)
|
|
13
13
|
submitters.push(impressionCountsSubmitter);
|
|
14
14
|
var telemetrySubmitter = telemetrySubmitterFactory(params);
|
|
15
|
-
if (params.
|
|
15
|
+
if (params.storage.uniqueKeys)
|
|
16
16
|
submitters.push(uniqueKeysSubmitterFactory(params));
|
|
17
17
|
return {
|
|
18
18
|
// `onlyTelemetry` true if SDK is created with userConsent not GRANTED
|
|
@@ -1,44 +1,11 @@
|
|
|
1
1
|
var _a, _b, _c;
|
|
2
2
|
import { submitterFactory, firstPushWindowDecorator } from './submitter';
|
|
3
|
-
import {
|
|
3
|
+
import { CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, NONE, DEBUG_ENUM, OPTIMIZED_ENUM, NONE_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
|
|
4
4
|
import { SDK_READY, SDK_READY_FROM_CACHE } from '../../readiness/constants';
|
|
5
5
|
import { base } from '../../utils/settingsValidation';
|
|
6
6
|
import { usedKeysMap } from '../../utils/inputValidation/apiKey';
|
|
7
7
|
import { timer } from '../../utils/timeTracker/timer';
|
|
8
8
|
import { objectAssign } from '../../utils/lang/objectAssign';
|
|
9
|
-
import { isStorageSync } from '../../trackers/impressionObserver/utils';
|
|
10
|
-
/**
|
|
11
|
-
* Converts data from telemetry cache into /metrics/usage request payload.
|
|
12
|
-
*/
|
|
13
|
-
export function telemetryCacheStatsAdapter(telemetry, splits, segments) {
|
|
14
|
-
return {
|
|
15
|
-
isEmpty: function () { return false; },
|
|
16
|
-
clear: function () { },
|
|
17
|
-
// @TODO consider moving inside telemetry cache for code size reduction
|
|
18
|
-
pop: function () {
|
|
19
|
-
return {
|
|
20
|
-
lS: telemetry.getLastSynchronization(),
|
|
21
|
-
mL: telemetry.popLatencies(),
|
|
22
|
-
mE: telemetry.popExceptions(),
|
|
23
|
-
hE: telemetry.popHttpErrors(),
|
|
24
|
-
hL: telemetry.popHttpLatencies(),
|
|
25
|
-
tR: telemetry.popTokenRefreshes(),
|
|
26
|
-
aR: telemetry.popAuthRejections(),
|
|
27
|
-
iQ: telemetry.getImpressionStats(QUEUED),
|
|
28
|
-
iDe: telemetry.getImpressionStats(DEDUPED),
|
|
29
|
-
iDr: telemetry.getImpressionStats(DROPPED),
|
|
30
|
-
spC: splits && splits.getSplitNames().length,
|
|
31
|
-
seC: segments && segments.getRegisteredSegments().length,
|
|
32
|
-
skC: segments && segments.getKeysCount(),
|
|
33
|
-
sL: telemetry.getSessionLength(),
|
|
34
|
-
eQ: telemetry.getEventStats(QUEUED),
|
|
35
|
-
eD: telemetry.getEventStats(DROPPED),
|
|
36
|
-
sE: telemetry.popStreamingEvents(),
|
|
37
|
-
t: telemetry.popTags(),
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
9
|
var OPERATION_MODE_MAP = (_a = {},
|
|
43
10
|
_a[STANDALONE_MODE] = STANDALONE_ENUM,
|
|
44
11
|
_a[CONSUMER_MODE] = CONSUMER_ENUM,
|
|
@@ -116,14 +83,12 @@ export function telemetryCacheConfigAdapter(telemetry, settings) {
|
|
|
116
83
|
* Submitter that periodically posts telemetry data
|
|
117
84
|
*/
|
|
118
85
|
export function telemetrySubmitterFactory(params) {
|
|
119
|
-
var
|
|
86
|
+
var telemetry = params.storage.telemetry, now = params.platform.now;
|
|
120
87
|
if (!telemetry || !now)
|
|
121
88
|
return; // No submitter created if telemetry cache is not defined
|
|
122
|
-
var settings = params.settings,
|
|
89
|
+
var settings = params.settings, _a = params.settings, log = _a.log, telemetryRefreshRate = _a.scheduler.telemetryRefreshRate, splitApi = params.splitApi, readiness = params.readiness, sdkReadinessManager = params.sdkReadinessManager;
|
|
123
90
|
var startTime = timer(now);
|
|
124
|
-
var submitter = firstPushWindowDecorator(submitterFactory(log, splitApi.postMetricsUsage,
|
|
125
|
-
// @TODO cannot provide splits and segments cache if they are async, because `submitterFactory` expects a sync storage source
|
|
126
|
-
isStorageSync(params.settings) ? telemetryCacheStatsAdapter(telemetry, splits, segments) : telemetryCacheStatsAdapter(telemetry), telemetryRefreshRate, 'telemetry stats', undefined, 0, true), telemetryRefreshRate);
|
|
91
|
+
var submitter = firstPushWindowDecorator(submitterFactory(log, splitApi.postMetricsUsage, telemetry, telemetryRefreshRate, 'telemetry stats', undefined, 0, true), telemetryRefreshRate);
|
|
127
92
|
readiness.gate.once(SDK_READY_FROM_CACHE, function () {
|
|
128
93
|
telemetry.recordTimeUntilReadyFromCache(startTime());
|
|
129
94
|
});
|
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE
|
|
2
|
-
/**
|
|
3
|
-
* Checks if impressions previous time should be added or not.
|
|
4
|
-
*/
|
|
5
|
-
export function shouldAddPt(settings) {
|
|
6
|
-
return [PRODUCER_MODE, STANDALONE_MODE, CONSUMER_PARTIAL_MODE].indexOf(settings.mode) > -1 ? true : false;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Checks if it should dedupe impressions or not.
|
|
10
|
-
*/
|
|
11
|
-
export function shouldBeOptimized(settings) {
|
|
12
|
-
if (!shouldAddPt(settings))
|
|
13
|
-
return false;
|
|
14
|
-
return settings.sync.impressionsMode === OPTIMIZED ? true : false;
|
|
15
|
-
}
|
|
1
|
+
import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../../utils/constants';
|
|
16
2
|
/**
|
|
17
3
|
* Storage is async if mode is consumer or partial consumer
|
|
18
4
|
*/
|
|
@@ -7,7 +7,7 @@ var noopFilterAdapter = {
|
|
|
7
7
|
/**
|
|
8
8
|
* Trackes uniques keys
|
|
9
9
|
* Unique Keys Tracker will be in charge of checking if the MTK was already sent to the BE in the last period
|
|
10
|
-
*
|
|
10
|
+
* or schedule to be sent; if not it will be added in an internal cache and sent in the next post.
|
|
11
11
|
*
|
|
12
12
|
* @param log Logger instance
|
|
13
13
|
* @param uniqueKeysCache cache to save unique keys
|
|
@@ -129,8 +129,8 @@ export function settingsValidation(config, validationParams) {
|
|
|
129
129
|
if (storage)
|
|
130
130
|
withDefaults.storage = storage(withDefaults);
|
|
131
131
|
// Validate key and TT (for client-side)
|
|
132
|
+
var maybeKey = withDefaults.core.key;
|
|
132
133
|
if (validationParams.acceptKey) {
|
|
133
|
-
var maybeKey = withDefaults.core.key;
|
|
134
134
|
// Although `key` is required in client-side, it can be omitted in LOCALHOST mode. In that case, the value `localhost_key` is used.
|
|
135
135
|
if (withDefaults.mode === LOCALHOST_MODE && maybeKey === undefined) {
|
|
136
136
|
withDefaults.core.key = 'localhost_key';
|
|
@@ -148,6 +148,12 @@ export function settingsValidation(config, validationParams) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
+
else {
|
|
152
|
+
// On server-side, key is undefined and used to distinguish from client-side
|
|
153
|
+
if (maybeKey !== undefined)
|
|
154
|
+
log.warn('Provided `key` is ignored in server-side SDK.'); // @ts-ignore
|
|
155
|
+
withDefaults.core.key = undefined;
|
|
156
|
+
}
|
|
151
157
|
// Current ip/hostname information
|
|
152
158
|
// @ts-ignore, modify readonly prop
|
|
153
159
|
withDefaults.runtime = runtime(withDefaults);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitsoftware/splitio-commons",
|
|
3
|
-
"version": "1.6.2-rc.
|
|
3
|
+
"version": "1.6.2-rc.14",
|
|
4
4
|
"description": "Split Javascript SDK common components",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"build:cjs": "rimraf cjs && tsc -m CommonJS --outDir cjs",
|
|
25
25
|
"test": "jest",
|
|
26
26
|
"test:coverage": "jest --coverage",
|
|
27
|
+
"all": "npm run check && npm run build && npm run test",
|
|
27
28
|
"publish:rc": "npm run check && npm run test && npm run build && npm publish --tag rc",
|
|
28
29
|
"publish:stable": "npm run check && npm run test && npm run build && npm publish"
|
|
29
30
|
},
|