@splitsoftware/splitio-commons 1.3.1 → 1.3.2-rc.2
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/consent/sdkUserConsent.js +1 -1
- package/cjs/listeners/browser.js +5 -4
- package/cjs/logger/constants.js +2 -1
- package/cjs/logger/messages/error.js +2 -1
- package/cjs/logger/messages/info.js +3 -3
- package/cjs/logger/messages/warn.js +2 -2
- package/cjs/sdkClient/client.js +17 -3
- package/cjs/sdkClient/sdkClient.js +4 -1
- package/cjs/sdkFactory/index.js +16 -19
- package/cjs/services/splitApi.js +15 -14
- package/cjs/services/splitHttpClient.js +4 -1
- package/cjs/storages/AbstractSegmentsCacheSync.js +0 -5
- package/cjs/storages/KeyBuilderSS.js +12 -19
- package/cjs/storages/findLatencyIndex.js +11 -6
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +13 -1
- package/cjs/storages/inLocalStorage/index.js +4 -1
- package/cjs/storages/inMemory/InMemoryStorage.js +2 -0
- package/cjs/storages/inMemory/InMemoryStorageCS.js +3 -0
- package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +6 -0
- package/cjs/storages/inMemory/SegmentsCacheInMemory.js +6 -0
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +165 -0
- package/cjs/storages/inRedis/TelemetryCacheInRedis.js +29 -0
- package/cjs/storages/inRedis/index.js +2 -4
- package/cjs/storages/pluggable/TelemetryCachePluggable.js +27 -0
- package/cjs/storages/pluggable/index.js +2 -1
- package/cjs/sync/polling/pollingManagerCS.js +1 -1
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +2 -2
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +0 -3
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -8
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +3 -6
- package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +20 -13
- package/cjs/sync/streaming/SSEHandler/index.js +21 -15
- package/cjs/sync/streaming/pushManager.js +7 -4
- package/cjs/sync/submitters/eventsSubmitter.js +28 -0
- package/cjs/sync/submitters/{impressionCountsSyncTask.js → impressionCountsSubmitter.js} +10 -7
- package/cjs/sync/submitters/{impressionsSyncTask.js → impressionsSubmitter.js} +8 -8
- package/cjs/sync/submitters/submitter.js +66 -0
- package/cjs/sync/submitters/submitterManager.js +12 -10
- package/cjs/sync/submitters/telemetrySubmitter.js +128 -0
- package/cjs/sync/syncManagerOnline.js +6 -2
- package/cjs/trackers/eventTracker.js +5 -1
- package/cjs/trackers/impressionsTracker.js +9 -1
- package/cjs/trackers/telemetryTracker.js +65 -0
- package/cjs/utils/constants/index.js +40 -1
- package/cjs/utils/inputValidation/apiKey.js +12 -11
- package/cjs/utils/settingsValidation/index.js +35 -11
- package/cjs/utils/settingsValidation/url.js +4 -0
- package/cjs/utils/timeTracker/index.js +1 -0
- package/cjs/utils/timeTracker/timer.js +2 -2
- package/esm/consent/sdkUserConsent.js +1 -1
- package/esm/listeners/browser.js +3 -2
- package/esm/logger/constants.js +1 -0
- package/esm/logger/messages/error.js +2 -1
- package/esm/logger/messages/info.js +3 -3
- package/esm/logger/messages/warn.js +2 -2
- package/esm/sdkClient/client.js +18 -4
- package/esm/sdkClient/sdkClient.js +4 -1
- package/esm/sdkFactory/index.js +16 -19
- package/esm/services/splitApi.js +15 -14
- package/esm/services/splitHttpClient.js +4 -1
- package/esm/storages/AbstractSegmentsCacheSync.js +0 -5
- package/esm/storages/KeyBuilderSS.js +12 -19
- package/esm/storages/findLatencyIndex.js +11 -6
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +13 -1
- package/esm/storages/inLocalStorage/index.js +5 -2
- package/esm/storages/inMemory/InMemoryStorage.js +3 -1
- package/esm/storages/inMemory/InMemoryStorageCS.js +4 -1
- package/esm/storages/inMemory/MySegmentsCacheInMemory.js +6 -0
- package/esm/storages/inMemory/SegmentsCacheInMemory.js +6 -0
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +161 -0
- package/esm/storages/inRedis/TelemetryCacheInRedis.js +26 -0
- package/esm/storages/inRedis/index.js +2 -4
- package/esm/storages/pluggable/TelemetryCachePluggable.js +24 -0
- package/esm/storages/pluggable/index.js +2 -1
- package/esm/sync/polling/pollingManagerCS.js +1 -1
- package/esm/sync/polling/syncTasks/splitsSyncTask.js +2 -2
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +0 -3
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -8
- package/esm/sync/polling/updaters/splitChangesUpdater.js +3 -6
- package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +8 -1
- package/esm/sync/streaming/SSEHandler/index.js +21 -15
- package/esm/sync/streaming/pushManager.js +7 -4
- package/esm/sync/submitters/eventsSubmitter.js +24 -0
- package/esm/sync/submitters/{impressionCountsSyncTask.js → impressionCountsSubmitter.js} +8 -5
- package/esm/sync/submitters/{impressionsSyncTask.js → impressionsSubmitter.js} +6 -6
- package/esm/sync/submitters/submitter.js +61 -0
- package/esm/sync/submitters/submitterManager.js +12 -10
- package/esm/sync/submitters/telemetrySubmitter.js +122 -0
- package/esm/sync/syncManagerOnline.js +6 -2
- package/esm/trackers/eventTracker.js +6 -2
- package/esm/trackers/impressionsTracker.js +10 -2
- package/esm/trackers/telemetryTracker.js +61 -0
- package/esm/utils/constants/index.js +38 -0
- package/esm/utils/inputValidation/apiKey.js +2 -1
- package/esm/utils/settingsValidation/index.js +34 -10
- package/esm/utils/settingsValidation/url.js +4 -0
- package/esm/utils/timeTracker/index.js +1 -0
- package/esm/utils/timeTracker/timer.js +2 -2
- package/package.json +1 -1
- package/src/consent/sdkUserConsent.ts +1 -1
- package/src/listeners/browser.ts +3 -2
- package/src/logger/constants.ts +1 -0
- package/src/logger/messages/error.ts +2 -1
- package/src/logger/messages/info.ts +3 -3
- package/src/logger/messages/warn.ts +2 -2
- package/src/sdkClient/client.ts +23 -4
- package/src/sdkClient/sdkClient.ts +4 -1
- package/src/sdkFactory/index.ts +22 -24
- package/src/sdkFactory/types.ts +32 -15
- package/src/services/splitApi.ts +17 -14
- package/src/services/splitHttpClient.ts +6 -3
- package/src/services/types.ts +7 -5
- package/src/storages/AbstractSegmentsCacheSync.ts +8 -3
- package/src/storages/KeyBuilderSS.ts +13 -50
- package/src/storages/findLatencyIndex.ts +12 -3
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +13 -1
- package/src/storages/inLocalStorage/index.ts +5 -2
- package/src/storages/inMemory/InMemoryStorage.ts +3 -1
- package/src/storages/inMemory/InMemoryStorageCS.ts +4 -1
- package/src/storages/inMemory/MySegmentsCacheInMemory.ts +8 -0
- package/src/storages/inMemory/SegmentsCacheInMemory.ts +6 -0
- package/src/storages/inMemory/TelemetryCacheInMemory.ts +210 -0
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +29 -0
- package/src/storages/inRedis/index.ts +2 -4
- package/src/storages/pluggable/TelemetryCachePluggable.ts +26 -0
- package/src/storages/pluggable/index.ts +2 -1
- package/src/storages/types.ts +84 -32
- package/src/sync/offline/syncManagerOffline.ts +4 -3
- package/src/sync/polling/pollingManagerCS.ts +3 -3
- package/src/sync/polling/pollingManagerSS.ts +2 -2
- package/src/sync/polling/syncTasks/splitsSyncTask.ts +2 -0
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +0 -4
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +2 -10
- package/src/sync/polling/updaters/splitChangesUpdater.ts +3 -6
- package/src/sync/streaming/SSEHandler/NotificationKeeper.ts +11 -1
- package/src/sync/streaming/SSEHandler/index.ts +21 -14
- package/src/sync/streaming/pushManager.ts +11 -7
- package/src/sync/submitters/eventsSubmitter.ts +35 -0
- package/src/sync/submitters/{impressionCountsSyncTask.ts → impressionCountsSubmitter.ts} +15 -15
- package/src/sync/submitters/{impressionsSyncTask.ts → impressionsSubmitter.ts} +12 -16
- package/src/sync/submitters/{submitterSyncTask.ts → submitter.ts} +34 -16
- package/src/sync/submitters/submitterManager.ts +14 -11
- package/src/sync/submitters/telemetrySubmitter.ts +143 -0
- package/src/sync/submitters/types.ts +123 -0
- package/src/sync/syncManagerOnline.ts +13 -7
- package/src/sync/types.ts +0 -15
- package/src/trackers/eventTracker.ts +7 -3
- package/src/trackers/impressionsTracker.ts +11 -3
- package/src/trackers/telemetryTracker.ts +63 -0
- package/src/trackers/types.ts +24 -0
- package/src/types.ts +35 -6
- package/src/utils/constants/index.ts +45 -0
- package/src/utils/inputValidation/apiKey.ts +2 -1
- package/src/utils/settingsValidation/index.ts +35 -11
- package/src/utils/settingsValidation/url.ts +4 -0
- package/src/utils/timeTracker/index.ts +1 -1
- package/src/utils/timeTracker/timer.ts +3 -3
- package/types/logger/constants.d.ts +1 -0
- package/types/sdkFactory/types.d.ts +29 -14
- package/types/services/splitApi.d.ts +2 -1
- package/types/services/types.d.ts +8 -5
- package/types/storages/AbstractSegmentsCacheSync.d.ts +7 -3
- package/types/storages/KeyBuilderSS.d.ts +3 -3
- package/types/storages/findLatencyIndex.d.ts +7 -1
- package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +2 -0
- package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +2 -0
- package/types/storages/inMemory/SegmentsCacheInMemory.d.ts +1 -0
- package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +6 -2
- package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +3 -3
- package/types/storages/pluggable/TelemetryCachePluggable.d.ts +2 -2
- package/types/storages/types.d.ts +71 -22
- package/types/sync/offline/syncManagerOffline.d.ts +3 -2
- package/types/sync/polling/pollingManagerCS.d.ts +2 -2
- package/types/sync/polling/pollingManagerSS.d.ts +2 -2
- package/types/sync/polling/syncTasks/splitsSyncTask.d.ts +1 -1
- package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
- package/types/sync/streaming/SSEHandler/NotificationKeeper.d.ts +2 -1
- package/types/sync/streaming/SSEHandler/index.d.ts +2 -1
- package/types/sync/streaming/pushManager.d.ts +2 -2
- package/types/sync/submitters/eventsSubmitter.d.ts +5 -0
- package/types/sync/submitters/impressionCountsSubmitter.d.ts +10 -0
- package/types/sync/submitters/impressionsSubmitter.d.ts +11 -0
- package/types/sync/submitters/submitter.d.ts +12 -0
- package/types/sync/submitters/submitterManager.d.ts +2 -2
- package/types/sync/submitters/telemetrySubmitter.d.ts +24 -0
- package/types/sync/submitters/telemetrySyncTask.d.ts +0 -27
- package/types/sync/submitters/types.d.ts +107 -0
- package/types/sync/syncManagerOnline.d.ts +3 -2
- package/types/sync/types.d.ts +0 -13
- package/types/trackers/eventTracker.d.ts +2 -2
- package/types/trackers/impressionsTracker.d.ts +2 -2
- package/types/trackers/telemetryTracker.d.ts +2 -3
- package/types/trackers/types.d.ts +22 -0
- package/types/types.d.ts +33 -4
- package/types/utils/constants/index.d.ts +37 -0
- package/types/utils/inputValidation/apiKey.d.ts +1 -0
- package/types/utils/settingsValidation/index.d.ts +40 -0
- package/types/utils/timeTracker/index.d.ts +1 -1
- package/types/utils/timeTracker/timer.d.ts +1 -1
- package/cjs/storages/inMemory/CountsCacheInMemory.js +0 -38
- package/cjs/storages/inMemory/LatenciesCacheInMemory.js +0 -43
- package/cjs/storages/inRedis/CountsCacheInRedis.js +0 -16
- package/cjs/storages/inRedis/LatenciesCacheInRedis.js +0 -18
- package/cjs/sync/submitters/eventsSyncTask.js +0 -44
- package/cjs/sync/submitters/metricsSyncTask.js +0 -31
- package/cjs/sync/submitters/submitterSyncTask.js +0 -44
- package/esm/storages/inMemory/CountsCacheInMemory.js +0 -35
- package/esm/storages/inMemory/LatenciesCacheInMemory.js +0 -40
- package/esm/storages/inRedis/CountsCacheInRedis.js +0 -13
- package/esm/storages/inRedis/LatenciesCacheInRedis.js +0 -15
- package/esm/sync/submitters/eventsSyncTask.js +0 -40
- package/esm/sync/submitters/metricsSyncTask.js +0 -26
- package/esm/sync/submitters/submitterSyncTask.js +0 -40
- package/src/storages/inMemory/CountsCacheInMemory.ts +0 -37
- package/src/storages/inMemory/LatenciesCacheInMemory.ts +0 -45
- package/src/storages/inRedis/CountsCacheInRedis.ts +0 -20
- package/src/storages/inRedis/LatenciesCacheInRedis.ts +0 -23
- package/src/sync/submitters/eventsSyncTask.ts +0 -57
- package/src/sync/submitters/metricsSyncTask.ts +0 -49
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { findLatencyIndex } from '../findLatencyIndex';
|
|
2
|
+
var MAX_STREAMING_EVENTS = 20;
|
|
3
|
+
var MAX_TAGS = 10;
|
|
4
|
+
function newBuckets() {
|
|
5
|
+
// MAX_LATENCY_BUCKET_COUNT (length) is 23;
|
|
6
|
+
return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
7
|
+
}
|
|
8
|
+
var ACCEPTANCE_RANGE = 0.001;
|
|
9
|
+
/**
|
|
10
|
+
* Used on client-side. 0.1% of instances will track telemetry
|
|
11
|
+
*/
|
|
12
|
+
export function shouldRecordTelemetry() {
|
|
13
|
+
return Math.random() <= ACCEPTANCE_RANGE;
|
|
14
|
+
}
|
|
15
|
+
var TelemetryCacheInMemory = /** @class */ (function () {
|
|
16
|
+
function TelemetryCacheInMemory() {
|
|
17
|
+
this.notReadyUsage = 0;
|
|
18
|
+
this.impressionStats = [0, 0, 0];
|
|
19
|
+
this.eventStats = [0, 0];
|
|
20
|
+
// @ts-expect-error
|
|
21
|
+
this.lastSync = {};
|
|
22
|
+
// @ts-expect-error
|
|
23
|
+
this.httpErrors = {};
|
|
24
|
+
// @ts-expect-error
|
|
25
|
+
this.httpLatencies = {};
|
|
26
|
+
this.authRejections = 0;
|
|
27
|
+
this.tokenRefreshes = 0;
|
|
28
|
+
this.streamingEvents = [];
|
|
29
|
+
this.tags = [];
|
|
30
|
+
// @ts-expect-error
|
|
31
|
+
this.exceptions = {};
|
|
32
|
+
// @ts-expect-error
|
|
33
|
+
this.latencies = {};
|
|
34
|
+
}
|
|
35
|
+
TelemetryCacheInMemory.prototype.getTimeUntilReady = function () {
|
|
36
|
+
return this.timeUntilReady;
|
|
37
|
+
};
|
|
38
|
+
TelemetryCacheInMemory.prototype.recordTimeUntilReady = function (ms) {
|
|
39
|
+
this.timeUntilReady = ms;
|
|
40
|
+
};
|
|
41
|
+
TelemetryCacheInMemory.prototype.getTimeUntilReadyFromCache = function () {
|
|
42
|
+
return this.timeUntilReadyFromCache;
|
|
43
|
+
};
|
|
44
|
+
TelemetryCacheInMemory.prototype.recordTimeUntilReadyFromCache = function (ms) {
|
|
45
|
+
this.timeUntilReadyFromCache = ms;
|
|
46
|
+
};
|
|
47
|
+
TelemetryCacheInMemory.prototype.getNonReadyUsage = function () {
|
|
48
|
+
return this.notReadyUsage;
|
|
49
|
+
};
|
|
50
|
+
TelemetryCacheInMemory.prototype.recordNonReadyUsage = function () {
|
|
51
|
+
this.notReadyUsage++;
|
|
52
|
+
};
|
|
53
|
+
TelemetryCacheInMemory.prototype.getImpressionStats = function (type) {
|
|
54
|
+
return this.impressionStats[type];
|
|
55
|
+
};
|
|
56
|
+
TelemetryCacheInMemory.prototype.recordImpressionStats = function (type, count) {
|
|
57
|
+
this.impressionStats[type] += count;
|
|
58
|
+
};
|
|
59
|
+
TelemetryCacheInMemory.prototype.getEventStats = function (type) {
|
|
60
|
+
return this.eventStats[type];
|
|
61
|
+
};
|
|
62
|
+
TelemetryCacheInMemory.prototype.recordEventStats = function (type, count) {
|
|
63
|
+
this.eventStats[type] += count;
|
|
64
|
+
};
|
|
65
|
+
TelemetryCacheInMemory.prototype.getLastSynchronization = function () {
|
|
66
|
+
return this.lastSync;
|
|
67
|
+
};
|
|
68
|
+
TelemetryCacheInMemory.prototype.recordSuccessfulSync = function (resource, timeMs) {
|
|
69
|
+
this.lastSync[resource] = timeMs;
|
|
70
|
+
};
|
|
71
|
+
TelemetryCacheInMemory.prototype.popHttpErrors = function () {
|
|
72
|
+
var result = this.httpErrors; // @ts-expect-error
|
|
73
|
+
this.httpErrors = {};
|
|
74
|
+
return result;
|
|
75
|
+
};
|
|
76
|
+
TelemetryCacheInMemory.prototype.recordHttpError = function (resource, status) {
|
|
77
|
+
if (!this.httpErrors[resource])
|
|
78
|
+
this.httpErrors[resource] = {};
|
|
79
|
+
if (!this.httpErrors[resource][status]) {
|
|
80
|
+
this.httpErrors[resource][status] = 1;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.httpErrors[resource][status]++;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
TelemetryCacheInMemory.prototype.popHttpLatencies = function () {
|
|
87
|
+
var result = this.httpLatencies; // @ts-expect-error
|
|
88
|
+
this.httpLatencies = {};
|
|
89
|
+
return result;
|
|
90
|
+
};
|
|
91
|
+
TelemetryCacheInMemory.prototype.recordHttpLatency = function (resource, latencyMs) {
|
|
92
|
+
if (!this.httpLatencies[resource]) {
|
|
93
|
+
this.httpLatencies[resource] = newBuckets();
|
|
94
|
+
}
|
|
95
|
+
this.httpLatencies[resource][findLatencyIndex(latencyMs)]++;
|
|
96
|
+
};
|
|
97
|
+
TelemetryCacheInMemory.prototype.popAuthRejections = function () {
|
|
98
|
+
var result = this.authRejections;
|
|
99
|
+
this.authRejections = 0;
|
|
100
|
+
return result;
|
|
101
|
+
};
|
|
102
|
+
TelemetryCacheInMemory.prototype.recordAuthRejections = function () {
|
|
103
|
+
this.authRejections++;
|
|
104
|
+
};
|
|
105
|
+
TelemetryCacheInMemory.prototype.popTokenRefreshes = function () {
|
|
106
|
+
var result = this.tokenRefreshes;
|
|
107
|
+
this.tokenRefreshes = 0;
|
|
108
|
+
return result;
|
|
109
|
+
};
|
|
110
|
+
TelemetryCacheInMemory.prototype.recordTokenRefreshes = function () {
|
|
111
|
+
this.tokenRefreshes++;
|
|
112
|
+
};
|
|
113
|
+
TelemetryCacheInMemory.prototype.popStreamingEvents = function () {
|
|
114
|
+
return this.streamingEvents.splice(0);
|
|
115
|
+
};
|
|
116
|
+
TelemetryCacheInMemory.prototype.recordStreamingEvents = function (streamingEvent) {
|
|
117
|
+
if (this.streamingEvents.length < MAX_STREAMING_EVENTS) {
|
|
118
|
+
this.streamingEvents.push(streamingEvent);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
TelemetryCacheInMemory.prototype.popTags = function () {
|
|
122
|
+
return this.tags.splice(0);
|
|
123
|
+
};
|
|
124
|
+
TelemetryCacheInMemory.prototype.addTag = function (tag) {
|
|
125
|
+
if (this.tags.length < MAX_TAGS) {
|
|
126
|
+
this.tags.push(tag);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
TelemetryCacheInMemory.prototype.getSessionLength = function () {
|
|
130
|
+
return this.sessionLength;
|
|
131
|
+
};
|
|
132
|
+
TelemetryCacheInMemory.prototype.recordSessionLength = function (ms) {
|
|
133
|
+
this.sessionLength = ms;
|
|
134
|
+
};
|
|
135
|
+
TelemetryCacheInMemory.prototype.popExceptions = function () {
|
|
136
|
+
var result = this.exceptions; // @ts-expect-error
|
|
137
|
+
this.exceptions = {};
|
|
138
|
+
return result;
|
|
139
|
+
};
|
|
140
|
+
TelemetryCacheInMemory.prototype.recordException = function (method) {
|
|
141
|
+
if (!this.exceptions[method]) {
|
|
142
|
+
this.exceptions[method] = 1;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
this.exceptions[method]++;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
TelemetryCacheInMemory.prototype.popLatencies = function () {
|
|
149
|
+
var result = this.latencies; // @ts-expect-error
|
|
150
|
+
this.latencies = {};
|
|
151
|
+
return result;
|
|
152
|
+
};
|
|
153
|
+
TelemetryCacheInMemory.prototype.recordLatency = function (method, latencyMs) {
|
|
154
|
+
if (!this.latencies[method]) {
|
|
155
|
+
this.latencies[method] = newBuckets();
|
|
156
|
+
}
|
|
157
|
+
this.latencies[method][findLatencyIndex(latencyMs)]++;
|
|
158
|
+
};
|
|
159
|
+
return TelemetryCacheInMemory;
|
|
160
|
+
}());
|
|
161
|
+
export { TelemetryCacheInMemory };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { findLatencyIndex } from '../findLatencyIndex';
|
|
2
|
+
var TelemetryCacheInRedis = /** @class */ (function () {
|
|
3
|
+
/**
|
|
4
|
+
* Create a Telemetry cache that uses Redis as storage.
|
|
5
|
+
* @param log Logger instance.
|
|
6
|
+
* @param keys Key builder.
|
|
7
|
+
* @param redis Redis client.
|
|
8
|
+
*/
|
|
9
|
+
function TelemetryCacheInRedis(log, keys, redis) {
|
|
10
|
+
this.log = log;
|
|
11
|
+
this.keys = keys;
|
|
12
|
+
this.redis = redis;
|
|
13
|
+
}
|
|
14
|
+
TelemetryCacheInRedis.prototype.recordLatency = function (method, latencyMs) {
|
|
15
|
+
var _a = this.keys.buildLatencyKey(method, findLatencyIndex(latencyMs)).split('::'), key = _a[0], field = _a[1];
|
|
16
|
+
return this.redis.hincrby(key, field, 1)
|
|
17
|
+
.catch(function () { });
|
|
18
|
+
};
|
|
19
|
+
TelemetryCacheInRedis.prototype.recordException = function (method) {
|
|
20
|
+
var _a = this.keys.buildExceptionKey(method).split('::'), key = _a[0], field = _a[1];
|
|
21
|
+
return this.redis.hincrby(key, field, 1)
|
|
22
|
+
.catch(function () { });
|
|
23
|
+
};
|
|
24
|
+
return TelemetryCacheInRedis;
|
|
25
|
+
}());
|
|
26
|
+
export { TelemetryCacheInRedis };
|
|
@@ -5,9 +5,8 @@ import { SplitsCacheInRedis } from './SplitsCacheInRedis';
|
|
|
5
5
|
import { SegmentsCacheInRedis } from './SegmentsCacheInRedis';
|
|
6
6
|
import { ImpressionsCacheInRedis } from './ImpressionsCacheInRedis';
|
|
7
7
|
import { EventsCacheInRedis } from './EventsCacheInRedis';
|
|
8
|
-
import { LatenciesCacheInRedis } from './LatenciesCacheInRedis';
|
|
9
|
-
import { CountsCacheInRedis } from './CountsCacheInRedis';
|
|
10
8
|
import { STORAGE_REDIS } from '../../utils/constants';
|
|
9
|
+
import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
|
|
11
10
|
/**
|
|
12
11
|
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.
|
|
13
12
|
* @see {@link https://www.npmjs.com/package/ioredis}
|
|
@@ -28,8 +27,7 @@ export function InRedisStorage(options) {
|
|
|
28
27
|
segments: new SegmentsCacheInRedis(log, keys, redisClient),
|
|
29
28
|
impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
|
|
30
29
|
events: new EventsCacheInRedis(log, keys.buildEventsKey(), redisClient, metadata),
|
|
31
|
-
|
|
32
|
-
counts: new CountsCacheInRedis(keys, redisClient),
|
|
30
|
+
telemetry: new TelemetryCacheInRedis(log, keys, redisClient),
|
|
33
31
|
// When using REDIS we should:
|
|
34
32
|
// 1- Disconnect from the storage
|
|
35
33
|
destroy: function () {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { findLatencyIndex } from '../findLatencyIndex';
|
|
2
|
+
var TelemetryCachePluggable = /** @class */ (function () {
|
|
3
|
+
/**
|
|
4
|
+
* Create a Telemetry cache that uses a storage wrapper.
|
|
5
|
+
* @param log Logger instance.
|
|
6
|
+
* @param keys Key builder.
|
|
7
|
+
* @param wrapper Adapted wrapper storage.
|
|
8
|
+
*/
|
|
9
|
+
function TelemetryCachePluggable(log, keys, wrapper) {
|
|
10
|
+
this.log = log;
|
|
11
|
+
this.keys = keys;
|
|
12
|
+
this.wrapper = wrapper;
|
|
13
|
+
}
|
|
14
|
+
TelemetryCachePluggable.prototype.recordLatency = function (method, latencyMs) {
|
|
15
|
+
return this.wrapper.incr(this.keys.buildLatencyKey(method, findLatencyIndex(latencyMs)))
|
|
16
|
+
.catch(function () { });
|
|
17
|
+
};
|
|
18
|
+
TelemetryCachePluggable.prototype.recordException = function (method) {
|
|
19
|
+
return this.wrapper.incr(this.keys.buildExceptionKey(method))
|
|
20
|
+
.catch(function () { });
|
|
21
|
+
};
|
|
22
|
+
return TelemetryCachePluggable;
|
|
23
|
+
}());
|
|
24
|
+
export { TelemetryCachePluggable };
|
|
@@ -63,7 +63,8 @@ export function PluggableStorage(options) {
|
|
|
63
63
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
64
64
|
impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined,
|
|
65
65
|
events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
|
|
66
|
-
// @TODO
|
|
66
|
+
// @TODO Not using TelemetryCachePluggable yet, because it is not supported by the Split Synchronizer
|
|
67
|
+
// telemetry: isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper),
|
|
67
68
|
// Disconnect the underlying storage
|
|
68
69
|
destroy: function () {
|
|
69
70
|
return wrapper.disconnect();
|
|
@@ -11,7 +11,7 @@ import { POLLING_SMART_PAUSING, POLLING_START, POLLING_STOP } from '../../logger
|
|
|
11
11
|
export function pollingManagerCSFactory(params) {
|
|
12
12
|
var splitApi = params.splitApi, storage = params.storage, readiness = params.readiness, settings = params.settings;
|
|
13
13
|
var log = settings.log;
|
|
14
|
-
var splitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings);
|
|
14
|
+
var splitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings, true);
|
|
15
15
|
// Map of matching keys to their corresponding MySegmentsSyncTask.
|
|
16
16
|
var mySegmentsSyncTasks = {};
|
|
17
17
|
var matchingKey = getMatching(settings.core.key);
|
|
@@ -4,6 +4,6 @@ import { splitChangesUpdaterFactory } from '../updaters/splitChangesUpdater';
|
|
|
4
4
|
/**
|
|
5
5
|
* Creates a sync task that periodically executes a `splitChangesUpdater` task
|
|
6
6
|
*/
|
|
7
|
-
export function splitsSyncTaskFactory(fetchSplitChanges, storage, readiness, settings) {
|
|
8
|
-
return syncTaskFactory(settings.log, splitChangesUpdaterFactory(settings.log, splitChangesFetcherFactory(fetchSplitChanges), storage.splits, storage.segments, readiness.splits, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady), settings.scheduler.featuresRefreshRate, 'splitChangesUpdater');
|
|
7
|
+
export function splitsSyncTaskFactory(fetchSplitChanges, storage, readiness, settings, isClientSide) {
|
|
8
|
+
return syncTaskFactory(settings.log, splitChangesUpdaterFactory(settings.log, splitChangesFetcherFactory(fetchSplitChanges), storage.splits, storage.segments, readiness.splits, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, isClientSide), settings.scheduler.featuresRefreshRate, 'splitChangesUpdater');
|
|
9
9
|
}
|
|
@@ -15,9 +15,6 @@ export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, splitsCache, my
|
|
|
15
15
|
if (startingUp)
|
|
16
16
|
promise = timeout(requestTimeoutBeforeReady, promise);
|
|
17
17
|
return promise;
|
|
18
|
-
// @TODO telemetry
|
|
19
|
-
// NOTE: We only collect metrics on startup.
|
|
20
|
-
// mySegmentsPromise = tracker.start(tracker.TaskNames.MY_SEGMENTS_FETCH, startingUp ? metricCollectors : false, mySegmentsPromise);
|
|
21
18
|
}
|
|
22
19
|
// @TODO if allowing pluggable storages, handle async execution
|
|
23
20
|
function updateSegments(segmentsData) {
|
|
@@ -15,13 +15,6 @@ import { thenable } from '../../../utils/promise/thenable';
|
|
|
15
15
|
*/
|
|
16
16
|
export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, readiness) {
|
|
17
17
|
var readyOnAlreadyExistentState = true;
|
|
18
|
-
/** telemetry decorator for `segmentChangesFetcher` promise */
|
|
19
|
-
function _promiseDecorator(promise) {
|
|
20
|
-
return promise;
|
|
21
|
-
// @TODO handle telemetry?
|
|
22
|
-
// const collectMetrics = startingUp || isNode; // If we are on the browser, only collect this metric for first fetch. On node do it always.
|
|
23
|
-
// splitsPromise = tracker.start(tracker.TaskNames.SPLITS_FETCH, collectMetrics ? metricCollectors : false, splitsPromise);
|
|
24
|
-
}
|
|
25
18
|
/**
|
|
26
19
|
* Segments updater returns a promise that resolves with a `false` boolean value if it fails at least to fetch a segment or synchronize it with the storage.
|
|
27
20
|
* Thus, a false result doesn't imply that SDK_SEGMENTS_ARRIVED was not emitted.
|
|
@@ -47,7 +40,7 @@ export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segment
|
|
|
47
40
|
// if fetchOnlyNew flag, avoid processing already fetched segments
|
|
48
41
|
if (fetchOnlyNew && since !== -1)
|
|
49
42
|
return -1;
|
|
50
|
-
return segmentChangesFetcher(since, segmentName, noCache
|
|
43
|
+
return segmentChangesFetcher(since, segmentName, noCache).then(function (changes) {
|
|
51
44
|
var changeNumber = -1;
|
|
52
45
|
var results = [];
|
|
53
46
|
changes.forEach(function (x) {
|
|
@@ -64,18 +64,15 @@ export function computeSplitsMutation(entries) {
|
|
|
64
64
|
* @param requestTimeoutBeforeReady How long the updater will wait for the request to timeout. Default 0, i.e., never timeout.
|
|
65
65
|
* @param retriesOnFailureBeforeReady How many retries on `/splitChanges` we the updater do in case of failure or timeout. Default 0, i.e., no retries.
|
|
66
66
|
*/
|
|
67
|
-
export function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, segments, splitsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady) {
|
|
67
|
+
export function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, segments, splitsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, isClientSide) {
|
|
68
68
|
if (requestTimeoutBeforeReady === void 0) { requestTimeoutBeforeReady = 0; }
|
|
69
69
|
if (retriesOnFailureBeforeReady === void 0) { retriesOnFailureBeforeReady = 0; }
|
|
70
70
|
var startingUp = true;
|
|
71
|
-
/** timeout
|
|
71
|
+
/** timeout decorator for `splitChangesFetcher` promise */
|
|
72
72
|
function _promiseDecorator(promise) {
|
|
73
73
|
if (startingUp && requestTimeoutBeforeReady)
|
|
74
74
|
promise = timeout(requestTimeoutBeforeReady, promise);
|
|
75
75
|
return promise;
|
|
76
|
-
// @TODO telemetry
|
|
77
|
-
// const collectMetrics = startingUp || isNode; // If we are on the browser, only collect this metric for first fetch. On node do it always.
|
|
78
|
-
// splitsPromise = tracker.start(tracker.TaskNames.SPLITS_FETCH, collectMetrics ? metricCollectors : false, splitsPromise);
|
|
79
76
|
}
|
|
80
77
|
/**
|
|
81
78
|
* SplitChanges updater returns a promise that resolves with a `false` boolean value if it fails to fetch splits or synchronize them with the storage.
|
|
@@ -109,7 +106,7 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, seg
|
|
|
109
106
|
]).then(function () {
|
|
110
107
|
if (splitsEventEmitter) {
|
|
111
108
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
112
|
-
return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && checkAllSegmentsExist(segments)))
|
|
109
|
+
return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && (isClientSide || checkAllSegmentsExist(segments))))
|
|
113
110
|
.catch(function () { return false; } /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
114
111
|
.then(function (emitSplitsArrivedEvent) {
|
|
115
112
|
// emit SDK events
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { CONNECTION_ESTABLISHED, DISABLED, ENABLED, OCCUPANCY_PRI, OCCUPANCY_SEC, PAUSED, STREAMING_STATUS } from '../../../utils/constants';
|
|
1
2
|
import { ControlType, PUSH_SUBSYSTEM_UP, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN } from '../constants';
|
|
2
3
|
var CONTROL_CHANNEL_REGEXS = [/control_pri$/, /control_sec$/];
|
|
4
|
+
var STREAMING_EVENT_TYPES = [OCCUPANCY_PRI, OCCUPANCY_SEC];
|
|
3
5
|
/**
|
|
4
6
|
* Factory of notification keeper, which process OCCUPANCY and CONTROL notifications and emits the corresponding push events.
|
|
5
7
|
*
|
|
6
8
|
* @param pushEmitter emitter for events related to streaming support
|
|
7
9
|
*/
|
|
8
10
|
// @TODO update logic to handle OCCUPANCY for any region and rename according to new spec (e.g.: PUSH_SUBSYSTEM_UP --> PUSH_SUBSYSTEM_UP)
|
|
9
|
-
export function notificationKeeperFactory(pushEmitter) {
|
|
11
|
+
export function notificationKeeperFactory(pushEmitter, telemetryTracker) {
|
|
10
12
|
var channels = CONTROL_CHANNEL_REGEXS.map(function (regex) { return ({
|
|
11
13
|
regex: regex,
|
|
12
14
|
hasPublishers: true,
|
|
@@ -22,6 +24,7 @@ export function notificationKeeperFactory(pushEmitter) {
|
|
|
22
24
|
}
|
|
23
25
|
return {
|
|
24
26
|
handleOpen: function () {
|
|
27
|
+
telemetryTracker.streamingEvent(CONNECTION_ESTABLISHED);
|
|
25
28
|
pushEmitter.emit(PUSH_SUBSYSTEM_UP);
|
|
26
29
|
},
|
|
27
30
|
isStreamingUp: function () {
|
|
@@ -31,6 +34,7 @@ export function notificationKeeperFactory(pushEmitter) {
|
|
|
31
34
|
for (var i = 0; i < channels.length; i++) {
|
|
32
35
|
var c = channels[i];
|
|
33
36
|
if (c.regex.test(channel)) {
|
|
37
|
+
telemetryTracker.streamingEvent(STREAMING_EVENT_TYPES[i], publishers);
|
|
34
38
|
if (timestamp > c.oTime) {
|
|
35
39
|
c.oTime = timestamp;
|
|
36
40
|
c.hasPublishers = publishers !== 0;
|
|
@@ -65,13 +69,16 @@ export function notificationKeeperFactory(pushEmitter) {
|
|
|
65
69
|
if (timestamp > c.cTime) {
|
|
66
70
|
c.cTime = timestamp;
|
|
67
71
|
if (controlType === ControlType.STREAMING_DISABLED) {
|
|
72
|
+
telemetryTracker.streamingEvent(STREAMING_STATUS, DISABLED);
|
|
68
73
|
pushEmitter.emit(PUSH_NONRETRYABLE_ERROR);
|
|
69
74
|
}
|
|
70
75
|
else if (hasPublishers) {
|
|
71
76
|
if (controlType === ControlType.STREAMING_PAUSED && hasResumed) {
|
|
77
|
+
telemetryTracker.streamingEvent(STREAMING_STATUS, PAUSED);
|
|
72
78
|
pushEmitter.emit(PUSH_SUBSYSTEM_DOWN);
|
|
73
79
|
}
|
|
74
80
|
else if (controlType === ControlType.STREAMING_RESUMED && !hasResumed) {
|
|
81
|
+
telemetryTracker.streamingEvent(STREAMING_STATUS, ENABLED);
|
|
75
82
|
pushEmitter.emit(PUSH_SUBSYSTEM_UP);
|
|
76
83
|
}
|
|
77
84
|
// nothing to do when hasPublishers === false:
|
|
@@ -2,27 +2,33 @@ import { errorParser, messageParser } from './NotificationParser';
|
|
|
2
2
|
import { notificationKeeperFactory } from './NotificationKeeper';
|
|
3
3
|
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE } from '../constants';
|
|
4
4
|
import { STREAMING_PARSING_ERROR_FAILS, ERROR_STREAMING_SSE, STREAMING_PARSING_MESSAGE_FAILS, STREAMING_NEW_MESSAGE } from '../../../logger/constants';
|
|
5
|
-
|
|
6
|
-
if (error.parsedData && error.parsedData.code) {
|
|
7
|
-
var code = error.parsedData.code;
|
|
8
|
-
// 401 errors due to invalid or expired token (e.g., if refresh token coudn't be executed)
|
|
9
|
-
if (40140 <= code && code <= 40149)
|
|
10
|
-
return true;
|
|
11
|
-
// Others 4XX errors (e.g., bad request from the SDK)
|
|
12
|
-
if (40000 <= code && code <= 49999)
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
// network errors or 5XX HTTP errors
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
5
|
+
import { ABLY_ERROR, NON_REQUESTED, SSE_CONNECTION_ERROR } from '../../../utils/constants';
|
|
18
6
|
/**
|
|
19
7
|
* Factory for SSEHandler, which processes SSEClient messages and emits the corresponding push events.
|
|
20
8
|
*
|
|
21
9
|
* @param log factory logger
|
|
22
10
|
* @param pushEmitter emitter for events related to streaming support
|
|
23
11
|
*/
|
|
24
|
-
export function SSEHandlerFactory(log, pushEmitter) {
|
|
25
|
-
var notificationKeeper = notificationKeeperFactory(pushEmitter);
|
|
12
|
+
export function SSEHandlerFactory(log, pushEmitter, telemetryTracker) {
|
|
13
|
+
var notificationKeeper = notificationKeeperFactory(pushEmitter, telemetryTracker);
|
|
14
|
+
function isRetryableError(error) {
|
|
15
|
+
if (error.parsedData && error.parsedData.code) {
|
|
16
|
+
// Ably error
|
|
17
|
+
var code = error.parsedData.code;
|
|
18
|
+
telemetryTracker.streamingEvent(ABLY_ERROR, code);
|
|
19
|
+
// 401 errors due to invalid or expired token (e.g., if refresh token coudn't be executed)
|
|
20
|
+
if (40140 <= code && code <= 40149)
|
|
21
|
+
return true;
|
|
22
|
+
// Others 4XX errors (e.g., bad request from the SDK)
|
|
23
|
+
if (40000 <= code && code <= 49999)
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// network errors or 5XX HTTP errors
|
|
28
|
+
telemetryTracker.streamingEvent(SSE_CONNECTION_ERROR, NON_REQUESTED);
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
26
32
|
return {
|
|
27
33
|
handleOpen: function () {
|
|
28
34
|
notificationKeeper.handleOpen();
|
|
@@ -14,13 +14,14 @@ import { UpdateStrategy } from './SSEHandler/types';
|
|
|
14
14
|
import { isInBitmap, parseBitmap, parseKeyList } from './mySegmentsV2utils';
|
|
15
15
|
import { _Set } from '../../utils/lang/sets';
|
|
16
16
|
import { hash64 } from '../../utils/murmur3/murmur3_64';
|
|
17
|
+
import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants';
|
|
17
18
|
/**
|
|
18
19
|
* PushManager factory:
|
|
19
20
|
* - for server-side if key is not provided in settings.
|
|
20
21
|
* - for client-side, with support for multiple clients, if key is provided in settings
|
|
21
22
|
*/
|
|
22
23
|
export function pushManagerFactory(params, pollingManager) {
|
|
23
|
-
var settings = params.settings, storage = params.storage, splitApi = params.splitApi, readiness = params.readiness, platform = params.platform;
|
|
24
|
+
var settings = params.settings, storage = params.storage, splitApi = params.splitApi, readiness = params.readiness, platform = params.platform, telemetryTracker = params.telemetryTracker;
|
|
24
25
|
// `userKey` is the matching key of main client in client-side SDK.
|
|
25
26
|
// It can be used to check if running on client-side or server-side SDK.
|
|
26
27
|
var userKey = settings.core.key ? getMatching(settings.core.key) : undefined;
|
|
@@ -37,7 +38,7 @@ export function pushManagerFactory(params, pollingManager) {
|
|
|
37
38
|
var authenticate = authenticateFactory(splitApi.fetchAuth);
|
|
38
39
|
// init feedback loop
|
|
39
40
|
var pushEmitter = new platform.EventEmitter();
|
|
40
|
-
var sseHandler = SSEHandlerFactory(log, pushEmitter);
|
|
41
|
+
var sseHandler = SSEHandlerFactory(log, pushEmitter, telemetryTracker);
|
|
41
42
|
sseClient.setEventHandler(sseHandler);
|
|
42
43
|
// init workers
|
|
43
44
|
// MySegmentsUpdateWorker (client-side) are initiated in `add` method
|
|
@@ -80,6 +81,7 @@ export function pushManagerFactory(params, pollingManager) {
|
|
|
80
81
|
return;
|
|
81
82
|
sseClient.open(authData);
|
|
82
83
|
}, connDelay * 1000);
|
|
84
|
+
telemetryTracker.streamingEvent(TOKEN_REFRESH, decodedToken.exp);
|
|
83
85
|
}
|
|
84
86
|
function connectPush() {
|
|
85
87
|
// Guard condition in case `stop/disconnectPush` has been called (e.g., calling SDK destroy, or app signal close/background)
|
|
@@ -110,6 +112,7 @@ export function pushManagerFactory(params, pollingManager) {
|
|
|
110
112
|
log.error(ERROR_STREAMING_AUTH, [error.message]);
|
|
111
113
|
// Handle 4XX HTTP errors: 401 (invalid API Key) or 400 (using incorrect API Key, i.e., client-side API Key on server-side)
|
|
112
114
|
if (error.statusCode >= 400 && error.statusCode < 500) {
|
|
115
|
+
telemetryTracker.streamingEvent(AUTH_REJECTION);
|
|
113
116
|
pushEmitter.emit(PUSH_NONRETRYABLE_ERROR);
|
|
114
117
|
return;
|
|
115
118
|
}
|
|
@@ -150,14 +153,14 @@ export function pushManagerFactory(params, pollingManager) {
|
|
|
150
153
|
connectPushRetryBackoff.reset();
|
|
151
154
|
stopWorkers();
|
|
152
155
|
});
|
|
153
|
-
/**
|
|
156
|
+
/** Fallback to polling without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */
|
|
154
157
|
pushEmitter.on(PUSH_NONRETRYABLE_ERROR, function handleNonRetryableError() {
|
|
155
158
|
disabled = true;
|
|
156
159
|
// Note: `stopWorkers` is been called twice, but it is not harmful
|
|
157
160
|
disconnectPush();
|
|
158
161
|
pushEmitter.emit(PUSH_SUBSYSTEM_DOWN); // no harm if polling already
|
|
159
162
|
});
|
|
160
|
-
/**
|
|
163
|
+
/** Fallback to polling with retry due to recoverable SSE and Authentication errors */
|
|
161
164
|
pushEmitter.on(PUSH_RETRYABLE_ERROR, function handleRetryableError() {
|
|
162
165
|
// SSE connection is closed to avoid repeated errors due to retries
|
|
163
166
|
sseClient.close();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { submitterFactory, firstPushWindowDecorator } from './submitter';
|
|
2
|
+
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
3
|
+
var DATA_NAME = 'events';
|
|
4
|
+
/**
|
|
5
|
+
* Submitter that periodically posts tracked events
|
|
6
|
+
*/
|
|
7
|
+
export function eventsSubmitterFactory(params) {
|
|
8
|
+
var _a = params.settings, log = _a.log, eventsPushRate = _a.scheduler.eventsPushRate, eventsFirstPushWindow = _a.startup.eventsFirstPushWindow, postEventsBulk = params.splitApi.postEventsBulk, events = params.storage.events;
|
|
9
|
+
// don't retry events.
|
|
10
|
+
var submitter = submitterFactory(log, postEventsBulk, events, eventsPushRate, DATA_NAME);
|
|
11
|
+
// Set a timer for the first push window of events.
|
|
12
|
+
if (eventsFirstPushWindow > 0)
|
|
13
|
+
submitter = firstPushWindowDecorator(submitter, eventsFirstPushWindow);
|
|
14
|
+
// register events submitter to be executed when events cache is full
|
|
15
|
+
events.setOnFullQueueCb(function () {
|
|
16
|
+
if (submitter.isRunning()) {
|
|
17
|
+
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
|
|
18
|
+
submitter.execute();
|
|
19
|
+
}
|
|
20
|
+
// If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
|
|
21
|
+
// Data will be sent when submitter is resumed.
|
|
22
|
+
});
|
|
23
|
+
return submitter;
|
|
24
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { submitterFactory } from './submitter';
|
|
2
2
|
/**
|
|
3
3
|
* Converts `impressionCounts` data from cache into request payload.
|
|
4
4
|
*/
|
|
@@ -22,9 +22,12 @@ export function fromImpressionCountsCollector(impressionsCount) {
|
|
|
22
22
|
}
|
|
23
23
|
var IMPRESSIONS_COUNT_RATE = 1800000; // 30 minutes
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* Submitter that periodically posts impression counts
|
|
26
26
|
*/
|
|
27
|
-
export function
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
export function impressionCountsSubmitterFactory(params) {
|
|
28
|
+
var log = params.settings.log, postTestImpressionsCount = params.splitApi.postTestImpressionsCount, impressionCounts = params.storage.impressionCounts;
|
|
29
|
+
if (impressionCounts) {
|
|
30
|
+
// retry impressions counts only once.
|
|
31
|
+
return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
|
|
32
|
+
}
|
|
30
33
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { groupBy, forOwn } from '../../utils/lang';
|
|
2
|
-
import {
|
|
2
|
+
import { submitterFactory } from './submitter';
|
|
3
3
|
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
4
4
|
var DATA_NAME = 'impressions';
|
|
5
5
|
/**
|
|
@@ -29,14 +29,14 @@ export function fromImpressionsCollector(sendLabels, data) {
|
|
|
29
29
|
return dto;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
32
|
+
* Submitter that periodically posts impressions data
|
|
33
33
|
*/
|
|
34
|
-
export function
|
|
35
|
-
|
|
34
|
+
export function impressionsSubmitterFactory(params) {
|
|
35
|
+
var _a = params.settings, log = _a.log, impressionsRefreshRate = _a.scheduler.impressionsRefreshRate, labelsEnabled = _a.core.labelsEnabled, postTestImpressionsBulk = params.splitApi.postTestImpressionsBulk, impressions = params.storage.impressions;
|
|
36
36
|
// retry impressions only once.
|
|
37
|
-
var syncTask =
|
|
37
|
+
var syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, DATA_NAME, fromImpressionsCollector.bind(undefined, labelsEnabled), 1);
|
|
38
38
|
// register impressions submitter to be executed when impressions cache is full
|
|
39
|
-
|
|
39
|
+
impressions.setOnFullQueueCb(function () {
|
|
40
40
|
if (syncTask.isRunning()) {
|
|
41
41
|
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
|
|
42
42
|
syncTask.execute();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { syncTaskFactory } from '../syncTask';
|
|
2
|
+
import { SUBMITTERS_PUSH, SUBMITTERS_PUSH_FAILS, SUBMITTERS_PUSH_RETRY } from '../../logger/constants';
|
|
3
|
+
/**
|
|
4
|
+
* Base function to create submitters, such as ImpressionsSubmitter and EventsSubmitter
|
|
5
|
+
*/
|
|
6
|
+
export function submitterFactory(log, postClient, sourceCache, postRate, dataName, fromCacheToPayload, maxRetries, debugLogs // true for telemetry submitters
|
|
7
|
+
) {
|
|
8
|
+
if (maxRetries === void 0) { maxRetries = 0; }
|
|
9
|
+
var retries = 0;
|
|
10
|
+
function postData() {
|
|
11
|
+
if (sourceCache.isEmpty())
|
|
12
|
+
return Promise.resolve();
|
|
13
|
+
var data = sourceCache.state();
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
var dataCountMessage = typeof data.length === 'number' ? data.length + " " + dataName : dataName;
|
|
16
|
+
log[debugLogs ? 'debug' : 'info'](SUBMITTERS_PUSH, [dataCountMessage]);
|
|
17
|
+
var jsonPayload = JSON.stringify(fromCacheToPayload ? fromCacheToPayload(data) : data);
|
|
18
|
+
if (!maxRetries)
|
|
19
|
+
sourceCache.clear();
|
|
20
|
+
return postClient(jsonPayload).then(function () {
|
|
21
|
+
retries = 0;
|
|
22
|
+
sourceCache.clear(); // we clear the queue if request successes.
|
|
23
|
+
}).catch(function (err) {
|
|
24
|
+
if (!maxRetries) {
|
|
25
|
+
log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
|
|
26
|
+
}
|
|
27
|
+
else if (retries === maxRetries) {
|
|
28
|
+
retries = 0;
|
|
29
|
+
sourceCache.clear(); // we clear the queue if request fails after retries.
|
|
30
|
+
log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
retries++;
|
|
34
|
+
log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_RETRY, [dataCountMessage, err]);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return syncTaskFactory(log, postData, postRate, dataName + ' submitter');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Decorates a provided submitter with a first execution window
|
|
42
|
+
*/
|
|
43
|
+
export function firstPushWindowDecorator(submitter, firstPushWindow) {
|
|
44
|
+
var running = false;
|
|
45
|
+
var stopEventPublisherTimeout;
|
|
46
|
+
var originalStart = submitter.start;
|
|
47
|
+
submitter.start = function () {
|
|
48
|
+
running = true;
|
|
49
|
+
stopEventPublisherTimeout = setTimeout(originalStart, firstPushWindow);
|
|
50
|
+
};
|
|
51
|
+
var originalStop = submitter.stop;
|
|
52
|
+
submitter.stop = function () {
|
|
53
|
+
running = false;
|
|
54
|
+
clearTimeout(stopEventPublisherTimeout);
|
|
55
|
+
originalStop();
|
|
56
|
+
};
|
|
57
|
+
submitter.isRunning = function () {
|
|
58
|
+
return running;
|
|
59
|
+
};
|
|
60
|
+
return submitter;
|
|
61
|
+
}
|