@splitsoftware/splitio-commons 1.6.2-rc.8 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CHANGES.txt +4 -1
  2. package/cjs/evaluator/index.js +5 -5
  3. package/cjs/listeners/browser.js +9 -11
  4. package/cjs/sdkClient/client.js +19 -7
  5. package/cjs/sdkFactory/index.js +7 -25
  6. package/cjs/services/splitApi.js +4 -4
  7. package/cjs/storages/AbstractSplitsCacheAsync.js +1 -1
  8. package/cjs/storages/AbstractSplitsCacheSync.js +1 -1
  9. package/cjs/storages/KeyBuilderSS.js +9 -9
  10. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +0 -1
  11. package/cjs/storages/inLocalStorage/index.js +15 -11
  12. package/cjs/storages/inMemory/InMemoryStorage.js +11 -8
  13. package/cjs/storages/inMemory/InMemoryStorageCS.js +11 -8
  14. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +65 -37
  15. package/cjs/storages/inMemory/{uniqueKeysCacheInMemory.js → UniqueKeysCacheInMemory.js} +24 -25
  16. package/cjs/storages/inMemory/{uniqueKeysCacheInMemoryCS.js → UniqueKeysCacheInMemoryCS.js} +10 -12
  17. package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
  18. package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +37 -2
  19. package/cjs/storages/inRedis/ImpressionsCacheInRedis.js +2 -19
  20. package/cjs/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  21. package/cjs/storages/inRedis/{uniqueKeysCacheInRedis.js → UniqueKeysCacheInRedis.js} +16 -4
  22. package/cjs/storages/inRedis/index.js +6 -4
  23. package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +81 -0
  24. package/cjs/storages/pluggable/ImpressionsCachePluggable.js +2 -19
  25. package/cjs/storages/pluggable/TelemetryCachePluggable.js +126 -0
  26. package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +61 -0
  27. package/cjs/storages/pluggable/inMemoryWrapper.js +8 -6
  28. package/cjs/storages/pluggable/index.js +51 -18
  29. package/cjs/storages/utils.js +73 -0
  30. package/cjs/sync/submitters/submitterManager.js +1 -1
  31. package/cjs/sync/submitters/telemetrySubmitter.js +4 -37
  32. package/cjs/sync/submitters/uniqueKeysSubmitter.js +4 -3
  33. package/cjs/trackers/impressionObserver/utils.js +1 -17
  34. package/cjs/trackers/uniqueKeysTracker.js +1 -1
  35. package/cjs/utils/lang/maps.js +15 -7
  36. package/cjs/utils/redis/RedisMock.js +31 -0
  37. package/cjs/utils/settingsValidation/index.js +7 -4
  38. package/esm/evaluator/index.js +5 -5
  39. package/esm/listeners/browser.js +9 -11
  40. package/esm/sdkClient/client.js +19 -7
  41. package/esm/sdkFactory/index.js +7 -25
  42. package/esm/services/splitApi.js +4 -4
  43. package/esm/storages/AbstractSplitsCacheAsync.js +1 -1
  44. package/esm/storages/AbstractSplitsCacheSync.js +1 -1
  45. package/esm/storages/KeyBuilderSS.js +8 -8
  46. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +0 -1
  47. package/esm/storages/inLocalStorage/index.js +16 -12
  48. package/esm/storages/inMemory/InMemoryStorage.js +13 -10
  49. package/esm/storages/inMemory/InMemoryStorageCS.js +12 -9
  50. package/esm/storages/inMemory/TelemetryCacheInMemory.js +64 -37
  51. package/esm/storages/inMemory/{uniqueKeysCacheInMemory.js → UniqueKeysCacheInMemory.js} +22 -24
  52. package/esm/storages/inMemory/{uniqueKeysCacheInMemoryCS.js → UniqueKeysCacheInMemoryCS.js} +10 -12
  53. package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
  54. package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +37 -2
  55. package/esm/storages/inRedis/ImpressionsCacheInRedis.js +2 -19
  56. package/esm/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  57. package/esm/storages/inRedis/{uniqueKeysCacheInRedis.js → UniqueKeysCacheInRedis.js} +15 -3
  58. package/esm/storages/inRedis/index.js +5 -3
  59. package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +78 -0
  60. package/esm/storages/pluggable/ImpressionsCachePluggable.js +2 -19
  61. package/esm/storages/pluggable/TelemetryCachePluggable.js +126 -0
  62. package/esm/storages/pluggable/UniqueKeysCachePluggable.js +58 -0
  63. package/esm/storages/pluggable/inMemoryWrapper.js +8 -6
  64. package/esm/storages/pluggable/index.js +52 -19
  65. package/esm/storages/utils.js +65 -0
  66. package/esm/sync/submitters/submitterManager.js +1 -1
  67. package/esm/sync/submitters/telemetrySubmitter.js +4 -36
  68. package/esm/sync/submitters/uniqueKeysSubmitter.js +4 -3
  69. package/esm/trackers/impressionObserver/utils.js +1 -15
  70. package/esm/trackers/uniqueKeysTracker.js +1 -1
  71. package/esm/utils/lang/maps.js +15 -7
  72. package/esm/utils/redis/RedisMock.js +28 -0
  73. package/esm/utils/settingsValidation/index.js +7 -4
  74. package/package.json +2 -2
  75. package/src/consent/sdkUserConsent.ts +1 -1
  76. package/src/evaluator/index.ts +6 -6
  77. package/src/listeners/browser.ts +9 -13
  78. package/src/logger/.DS_Store +0 -0
  79. package/src/sdkClient/client.ts +21 -8
  80. package/src/sdkClient/sdkClient.ts +1 -1
  81. package/src/sdkFactory/index.ts +10 -33
  82. package/src/sdkFactory/types.ts +2 -2
  83. package/src/services/splitApi.ts +6 -6
  84. package/src/services/types.ts +2 -2
  85. package/src/storages/AbstractSplitsCacheAsync.ts +1 -1
  86. package/src/storages/AbstractSplitsCacheSync.ts +1 -1
  87. package/src/storages/KeyBuilderSS.ts +13 -11
  88. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +0 -1
  89. package/src/storages/inLocalStorage/index.ts +17 -12
  90. package/src/storages/inMemory/AttributesCacheInMemory.ts +7 -7
  91. package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +2 -2
  92. package/src/storages/inMemory/InMemoryStorage.ts +14 -10
  93. package/src/storages/inMemory/InMemoryStorageCS.ts +13 -10
  94. package/src/storages/inMemory/TelemetryCacheInMemory.ts +72 -35
  95. package/src/storages/inMemory/{uniqueKeysCacheInMemory.ts → UniqueKeysCacheInMemory.ts} +26 -28
  96. package/src/storages/inMemory/{uniqueKeysCacheInMemoryCS.ts → UniqueKeysCacheInMemoryCS.ts} +15 -17
  97. package/src/storages/inRedis/EventsCacheInRedis.ts +1 -1
  98. package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +51 -8
  99. package/src/storages/inRedis/ImpressionsCacheInRedis.ts +2 -22
  100. package/src/storages/inRedis/TelemetryCacheInRedis.ts +122 -1
  101. package/src/storages/inRedis/{uniqueKeysCacheInRedis.ts → UniqueKeysCacheInRedis.ts} +25 -12
  102. package/src/storages/inRedis/index.ts +6 -3
  103. package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +92 -0
  104. package/src/storages/pluggable/ImpressionsCachePluggable.ts +3 -23
  105. package/src/storages/pluggable/TelemetryCachePluggable.ts +147 -1
  106. package/src/storages/pluggable/UniqueKeysCachePluggable.ts +67 -0
  107. package/src/storages/pluggable/inMemoryWrapper.ts +6 -6
  108. package/src/storages/pluggable/index.ts +56 -20
  109. package/src/storages/types.ts +53 -70
  110. package/src/storages/utils.ts +78 -0
  111. package/src/sync/submitters/submitter.ts +2 -2
  112. package/src/sync/submitters/submitterManager.ts +1 -1
  113. package/src/sync/submitters/telemetrySubmitter.ts +9 -39
  114. package/src/sync/submitters/types.ts +33 -17
  115. package/src/sync/submitters/uniqueKeysSubmitter.ts +6 -5
  116. package/src/trackers/impressionObserver/utils.ts +1 -16
  117. package/src/trackers/impressionsTracker.ts +2 -2
  118. package/src/trackers/strategy/strategyDebug.ts +4 -4
  119. package/src/trackers/strategy/strategyNone.ts +9 -9
  120. package/src/trackers/strategy/strategyOptimized.ts +9 -9
  121. package/src/trackers/uniqueKeysTracker.ts +6 -6
  122. package/src/types.ts +0 -2
  123. package/src/utils/lang/maps.ts +20 -8
  124. package/src/utils/redis/RedisMock.ts +33 -0
  125. package/src/utils/settingsValidation/index.ts +5 -5
  126. package/types/services/types.d.ts +2 -2
  127. package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
  128. package/types/storages/AbstractSplitsCacheSync.d.ts +1 -1
  129. package/types/storages/KeyBuilderSS.d.ts +5 -2
  130. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +0 -1
  131. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +23 -9
  132. package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +9 -9
  133. package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +2 -4
  134. package/types/storages/inRedis/EventsCacheInRedis.d.ts +1 -1
  135. package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +3 -1
  136. package/types/storages/inRedis/ImpressionsCacheInRedis.d.ts +0 -1
  137. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +16 -1
  138. package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +8 -2
  139. package/types/storages/pluggable/ImpressionCountsCachePluggable.d.ts +16 -0
  140. package/types/storages/pluggable/ImpressionsCachePluggable.d.ts +1 -2
  141. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +17 -1
  142. package/types/storages/pluggable/UniqueKeysCachePluggable.d.ts +20 -0
  143. package/types/storages/types.d.ts +42 -49
  144. package/types/storages/utils.d.ts +8 -0
  145. package/types/sync/submitters/submitter.d.ts +2 -2
  146. package/types/sync/submitters/telemetrySubmitter.d.ts +2 -10
  147. package/types/sync/submitters/types.d.ts +27 -18
  148. package/types/trackers/impressionObserver/utils.d.ts +0 -8
  149. package/types/trackers/strategy/strategyNone.d.ts +2 -2
  150. package/types/trackers/strategy/strategyOptimized.d.ts +2 -2
  151. package/types/trackers/uniqueKeysTracker.d.ts +1 -1
  152. package/types/types.d.ts +0 -2
  153. package/types/utils/lang/maps.d.ts +6 -2
  154. package/types/utils/redis/RedisMock.d.ts +4 -0
  155. package/types/utils/settingsValidation/index.d.ts +0 -1
  156. package/cjs/storages/metadataBuilder.js +0 -12
  157. package/esm/storages/metadataBuilder.js +0 -8
  158. package/src/storages/metadataBuilder.ts +0 -11
  159. package/types/sdkClient/types.d.ts +0 -18
  160. package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
  161. package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
  162. package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
  163. package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
  164. package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
  165. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
  166. package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
  167. package/types/sync/submitters/impressionCountsSubmitterInRedis.d.ts +0 -5
  168. package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
  169. package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
  170. package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
  171. package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
  172. package/types/sync/submitters/uniqueKeysSubmitterInRedis.d.ts +0 -5
  173. package/types/sync/syncTaskComposite.d.ts +0 -5
  174. package/types/trackers/filter/bloomFilter.d.ts +0 -10
  175. package/types/trackers/filter/dictionaryFilter.d.ts +0 -8
  176. package/types/trackers/filter/types.d.ts +0 -5
  177. package/types/utils/timeTracker/index.d.ts +0 -70
@@ -1,37 +1,67 @@
1
+ import { DEDUPED, DROPPED, LOCALHOST_MODE, QUEUED } from '../../utils/constants';
1
2
  import { findLatencyIndex } from '../findLatencyIndex';
2
3
  var MAX_STREAMING_EVENTS = 20;
3
4
  var MAX_TAGS = 10;
4
- function newBuckets() {
5
- // MAX_LATENCY_BUCKET_COUNT (length) is 23;
5
+ export var MAX_LATENCY_BUCKET_COUNT = 23;
6
+ export function newBuckets() {
7
+ // MAX_LATENCY_BUCKET_COUNT (length) is 23
8
+ // Not using Array.fill for old browsers compatibility
6
9
  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
10
  }
8
11
  var ACCEPTANCE_RANGE = 0.001;
9
12
  /**
10
- * Used on client-side. 0.1% of instances will track telemetry
13
+ * Record telemetry if mode is not localhost.
14
+ * All factory instances track telemetry on server-side, and 0.1% on client-side.
11
15
  */
12
- export function shouldRecordTelemetry() {
13
- return Math.random() <= ACCEPTANCE_RANGE;
16
+ export function shouldRecordTelemetry(_a) {
17
+ var settings = _a.settings;
18
+ return settings.mode !== LOCALHOST_MODE && (settings.core.key === undefined || Math.random() <= ACCEPTANCE_RANGE);
14
19
  }
15
20
  var TelemetryCacheInMemory = /** @class */ (function () {
16
- function TelemetryCacheInMemory() {
21
+ function TelemetryCacheInMemory(splits, segments) {
22
+ this.splits = splits;
23
+ this.segments = segments;
24
+ // isEmpty flag
25
+ this.e = true;
17
26
  this.notReadyUsage = 0;
27
+ /** Usage stats */
18
28
  this.impressionStats = [0, 0, 0];
19
29
  this.eventStats = [0, 0];
20
- // @ts-expect-error
21
30
  this.lastSync = {};
22
- // @ts-expect-error
23
31
  this.httpErrors = {};
24
- // @ts-expect-error
25
32
  this.httpLatencies = {};
26
33
  this.authRejections = 0;
27
34
  this.tokenRefreshes = 0;
28
35
  this.streamingEvents = [];
29
36
  this.tags = [];
30
- // @ts-expect-error
31
37
  this.exceptions = {};
32
- // @ts-expect-error
33
38
  this.latencies = {};
34
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
+ };
35
65
  TelemetryCacheInMemory.prototype.getTimeUntilReady = function () {
36
66
  return this.timeUntilReady;
37
67
  };
@@ -55,44 +85,41 @@ var TelemetryCacheInMemory = /** @class */ (function () {
55
85
  };
56
86
  TelemetryCacheInMemory.prototype.recordImpressionStats = function (type, count) {
57
87
  this.impressionStats[type] += count;
88
+ this.e = false;
58
89
  };
59
90
  TelemetryCacheInMemory.prototype.getEventStats = function (type) {
60
91
  return this.eventStats[type];
61
92
  };
62
93
  TelemetryCacheInMemory.prototype.recordEventStats = function (type, count) {
63
94
  this.eventStats[type] += count;
95
+ this.e = false;
64
96
  };
65
97
  TelemetryCacheInMemory.prototype.getLastSynchronization = function () {
66
98
  return this.lastSync;
67
99
  };
68
100
  TelemetryCacheInMemory.prototype.recordSuccessfulSync = function (resource, timeMs) {
69
101
  this.lastSync[resource] = timeMs;
102
+ this.e = false;
70
103
  };
71
104
  TelemetryCacheInMemory.prototype.popHttpErrors = function () {
72
- var result = this.httpErrors; // @ts-expect-error
105
+ var result = this.httpErrors;
73
106
  this.httpErrors = {};
74
107
  return result;
75
108
  };
76
109
  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
- }
110
+ var statusErrors = (this.httpErrors[resource] = this.httpErrors[resource] || {});
111
+ statusErrors[status] = (statusErrors[status] || 0) + 1;
112
+ this.e = false;
85
113
  };
86
114
  TelemetryCacheInMemory.prototype.popHttpLatencies = function () {
87
- var result = this.httpLatencies; // @ts-expect-error
115
+ var result = this.httpLatencies;
88
116
  this.httpLatencies = {};
89
117
  return result;
90
118
  };
91
119
  TelemetryCacheInMemory.prototype.recordHttpLatency = function (resource, latencyMs) {
92
- if (!this.httpLatencies[resource]) {
93
- this.httpLatencies[resource] = newBuckets();
94
- }
95
- this.httpLatencies[resource][findLatencyIndex(latencyMs)]++;
120
+ var latencyBuckets = (this.httpLatencies[resource] = this.httpLatencies[resource] || newBuckets());
121
+ latencyBuckets[findLatencyIndex(latencyMs)]++;
122
+ this.e = false;
96
123
  };
97
124
  TelemetryCacheInMemory.prototype.popAuthRejections = function () {
98
125
  var result = this.authRejections;
@@ -101,6 +128,7 @@ var TelemetryCacheInMemory = /** @class */ (function () {
101
128
  };
102
129
  TelemetryCacheInMemory.prototype.recordAuthRejections = function () {
103
130
  this.authRejections++;
131
+ this.e = false;
104
132
  };
105
133
  TelemetryCacheInMemory.prototype.popTokenRefreshes = function () {
106
134
  var result = this.tokenRefreshes;
@@ -109,6 +137,7 @@ var TelemetryCacheInMemory = /** @class */ (function () {
109
137
  };
110
138
  TelemetryCacheInMemory.prototype.recordTokenRefreshes = function () {
111
139
  this.tokenRefreshes++;
140
+ this.e = false;
112
141
  };
113
142
  TelemetryCacheInMemory.prototype.popStreamingEvents = function () {
114
143
  return this.streamingEvents.splice(0);
@@ -117,6 +146,7 @@ var TelemetryCacheInMemory = /** @class */ (function () {
117
146
  if (this.streamingEvents.length < MAX_STREAMING_EVENTS) {
118
147
  this.streamingEvents.push(streamingEvent);
119
148
  }
149
+ this.e = false;
120
150
  };
121
151
  TelemetryCacheInMemory.prototype.popTags = function () {
122
152
  return this.tags.splice(0);
@@ -125,36 +155,33 @@ var TelemetryCacheInMemory = /** @class */ (function () {
125
155
  if (this.tags.length < MAX_TAGS) {
126
156
  this.tags.push(tag);
127
157
  }
158
+ this.e = false;
128
159
  };
129
160
  TelemetryCacheInMemory.prototype.getSessionLength = function () {
130
161
  return this.sessionLength;
131
162
  };
132
163
  TelemetryCacheInMemory.prototype.recordSessionLength = function (ms) {
133
164
  this.sessionLength = ms;
165
+ this.e = false;
134
166
  };
135
167
  TelemetryCacheInMemory.prototype.popExceptions = function () {
136
- var result = this.exceptions; // @ts-expect-error
168
+ var result = this.exceptions;
137
169
  this.exceptions = {};
138
170
  return result;
139
171
  };
140
172
  TelemetryCacheInMemory.prototype.recordException = function (method) {
141
- if (!this.exceptions[method]) {
142
- this.exceptions[method] = 1;
143
- }
144
- else {
145
- this.exceptions[method]++;
146
- }
173
+ this.exceptions[method] = (this.exceptions[method] || 0) + 1;
174
+ this.e = false;
147
175
  };
148
176
  TelemetryCacheInMemory.prototype.popLatencies = function () {
149
- var result = this.latencies; // @ts-expect-error
177
+ var result = this.latencies;
150
178
  this.latencies = {};
151
179
  return result;
152
180
  };
153
181
  TelemetryCacheInMemory.prototype.recordLatency = function (method, latencyMs) {
154
- if (!this.latencies[method]) {
155
- this.latencies[method] = newBuckets();
156
- }
157
- this.latencies[method][findLatencyIndex(latencyMs)]++;
182
+ var latencyBuckets = (this.latencies[method] = this.latencies[method] || newBuckets());
183
+ latencyBuckets[findLatencyIndex(latencyMs)]++;
184
+ this.e = false;
158
185
  };
159
186
  return TelemetryCacheInMemory;
160
187
  }());
@@ -1,5 +1,22 @@
1
1
  import { setToArray, _Set } from '../../utils/lang/sets';
2
2
  import { DEFAULT_CACHE_SIZE } from '../inRedis/constants';
3
+ /**
4
+ * Converts `uniqueKeys` data from cache into request payload for SS.
5
+ */
6
+ export function fromUniqueKeysCollector(uniqueKeys) {
7
+ var payload = [];
8
+ var featureNames = Object.keys(uniqueKeys);
9
+ for (var i = 0; i < featureNames.length; i++) {
10
+ var featureName = featureNames[i];
11
+ var userKeys = setToArray(uniqueKeys[featureName]);
12
+ var uniqueKeysPayload = {
13
+ f: featureName,
14
+ ks: userKeys
15
+ };
16
+ payload.push(uniqueKeysPayload);
17
+ }
18
+ return { keys: payload };
19
+ }
3
20
  var UniqueKeysCacheInMemory = /** @class */ (function () {
4
21
  function UniqueKeysCacheInMemory(uniqueKeysQueueSize) {
5
22
  if (uniqueKeysQueueSize === void 0) { uniqueKeysQueueSize = DEFAULT_CACHE_SIZE; }
@@ -11,16 +28,14 @@ var UniqueKeysCacheInMemory = /** @class */ (function () {
11
28
  this.onFullQueue = cb;
12
29
  };
13
30
  /**
14
- * Store unique keys in sequential order
15
- * key: string = feature name.
16
- * value: Set<string> = set of unique keys.
31
+ * Store unique keys per feature.
17
32
  */
18
- UniqueKeysCacheInMemory.prototype.track = function (key, featureName) {
33
+ UniqueKeysCacheInMemory.prototype.track = function (userKey, featureName) {
19
34
  if (!this.uniqueKeysTracker[featureName])
20
35
  this.uniqueKeysTracker[featureName] = new _Set();
21
36
  var tracker = this.uniqueKeysTracker[featureName];
22
- if (!tracker.has(key)) {
23
- tracker.add(key);
37
+ if (!tracker.has(userKey)) {
38
+ tracker.add(userKey);
24
39
  this.uniqueTrackerSize++;
25
40
  }
26
41
  if (this.uniqueTrackerSize >= this.maxStorage && this.onFullQueue) {
@@ -40,7 +55,7 @@ var UniqueKeysCacheInMemory = /** @class */ (function () {
40
55
  UniqueKeysCacheInMemory.prototype.pop = function () {
41
56
  var data = this.uniqueKeysTracker;
42
57
  this.uniqueKeysTracker = {};
43
- return this.fromUniqueKeysCollector(data);
58
+ return fromUniqueKeysCollector(data);
44
59
  };
45
60
  /**
46
61
  * Check if the cache is empty.
@@ -48,23 +63,6 @@ var UniqueKeysCacheInMemory = /** @class */ (function () {
48
63
  UniqueKeysCacheInMemory.prototype.isEmpty = function () {
49
64
  return Object.keys(this.uniqueKeysTracker).length === 0;
50
65
  };
51
- /**
52
- * Converts `uniqueKeys` data from cache into request payload for SS.
53
- */
54
- UniqueKeysCacheInMemory.prototype.fromUniqueKeysCollector = function (uniqueKeys) {
55
- var payload = [];
56
- var featureNames = Object.keys(uniqueKeys);
57
- for (var i = 0; i < featureNames.length; i++) {
58
- var featureName = featureNames[i];
59
- var featureKeys = setToArray(uniqueKeys[featureName]);
60
- var uniqueKeysPayload = {
61
- f: featureName,
62
- ks: featureKeys
63
- };
64
- payload.push(uniqueKeysPayload);
65
- }
66
- return { keys: payload };
67
- };
68
66
  return UniqueKeysCacheInMemory;
69
67
  }());
70
68
  export { UniqueKeysCacheInMemory };
@@ -16,14 +16,12 @@ var UniqueKeysCacheInMemoryCS = /** @class */ (function () {
16
16
  this.onFullQueue = cb;
17
17
  };
18
18
  /**
19
- * Store unique keys in sequential order
20
- * key: string = key.
21
- * value: HashSet<string> = set of split names.
19
+ * Store unique keys per feature.
22
20
  */
23
- UniqueKeysCacheInMemoryCS.prototype.track = function (key, featureName) {
24
- if (!this.uniqueKeysTracker[key])
25
- this.uniqueKeysTracker[key] = new _Set();
26
- var tracker = this.uniqueKeysTracker[key];
21
+ UniqueKeysCacheInMemoryCS.prototype.track = function (userKey, featureName) {
22
+ if (!this.uniqueKeysTracker[userKey])
23
+ this.uniqueKeysTracker[userKey] = new _Set();
24
+ var tracker = this.uniqueKeysTracker[userKey];
27
25
  if (!tracker.has(featureName)) {
28
26
  tracker.add(featureName);
29
27
  this.uniqueTrackerSize++;
@@ -58,12 +56,12 @@ var UniqueKeysCacheInMemoryCS = /** @class */ (function () {
58
56
  */
59
57
  UniqueKeysCacheInMemoryCS.prototype.fromUniqueKeysCollector = function (uniqueKeys) {
60
58
  var payload = [];
61
- var featureKeys = Object.keys(uniqueKeys);
62
- for (var k = 0; k < featureKeys.length; k++) {
63
- var featureKey = featureKeys[k];
64
- var featureNames = setToArray(uniqueKeys[featureKey]);
59
+ var userKeys = Object.keys(uniqueKeys);
60
+ for (var k = 0; k < userKeys.length; k++) {
61
+ var userKey = userKeys[k];
62
+ var featureNames = setToArray(uniqueKeys[userKey]);
65
63
  var uniqueKeysPayload = {
66
- k: featureKey,
64
+ k: userKey,
67
65
  fs: featureNames
68
66
  };
69
67
  payload.push(uniqueKeysPayload);
@@ -39,7 +39,7 @@ var EventsCacheInRedis = /** @class */ (function () {
39
39
  };
40
40
  /**
41
41
  * Pop the given number of events from the storage.
42
- * The returned promise rejects if the wrapper operation fails.
42
+ * The returned promise rejects if the redis operation fails.
43
43
  *
44
44
  * NOTE: this method doesn't take into account MAX_EVENT_SIZE or MAX_QUEUE_BYTE_SIZE limits.
45
45
  * It is the submitter responsability to handle that.
@@ -1,4 +1,5 @@
1
1
  import { __extends } from "tslib";
2
+ import { forOwn } from '../../utils/lang';
2
3
  import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
3
4
  import { LOG_PREFIX, REFRESH_RATE, TTL_REFRESH } from './constants';
4
5
  var ImpressionCountsCacheInRedis = /** @class */ (function (_super) {
@@ -17,7 +18,7 @@ var ImpressionCountsCacheInRedis = /** @class */ (function (_super) {
17
18
  var _this = this;
18
19
  var counts = this.pop();
19
20
  var keys = Object.keys(counts);
20
- if (!keys)
21
+ if (!keys.length)
21
22
  return Promise.resolve(false);
22
23
  var pipeline = this.redis.pipeline();
23
24
  keys.forEach(function (key) {
@@ -32,7 +33,7 @@ var ImpressionCountsCacheInRedis = /** @class */ (function (_super) {
32
33
  })
33
34
  .catch(function (err) {
34
35
  _this.log.error(LOG_PREFIX + "Error in impression counts pipeline: " + err + ".");
35
- return Promise.resolve(false);
36
+ return false;
36
37
  });
37
38
  };
38
39
  ImpressionCountsCacheInRedis.prototype.start = function () {
@@ -42,6 +43,40 @@ var ImpressionCountsCacheInRedis = /** @class */ (function (_super) {
42
43
  clearInterval(this.intervalId);
43
44
  return this.postImpressionCountsInRedis();
44
45
  };
46
+ // Async consumer API, used by synchronizer
47
+ ImpressionCountsCacheInRedis.prototype.getImpressionsCount = function () {
48
+ var _this = this;
49
+ return this.redis.hgetall(this.key)
50
+ .then(function (counts) {
51
+ if (!Object.keys(counts).length)
52
+ return undefined;
53
+ _this.redis.del(_this.key).catch(function () { });
54
+ var pf = [];
55
+ forOwn(counts, function (count, key) {
56
+ var nameAndTime = key.split('::');
57
+ if (nameAndTime.length !== 2) {
58
+ _this.log.error(LOG_PREFIX + "Error spliting key " + key);
59
+ return;
60
+ }
61
+ var timeFrame = parseInt(nameAndTime[1]);
62
+ if (isNaN(timeFrame)) {
63
+ _this.log.error(LOG_PREFIX + "Error parsing time frame " + nameAndTime[1]);
64
+ return;
65
+ }
66
+ var rawCount = parseInt(count);
67
+ if (isNaN(rawCount)) {
68
+ _this.log.error(LOG_PREFIX + "Error parsing raw count " + count);
69
+ return;
70
+ }
71
+ pf.push({
72
+ f: nameAndTime[0],
73
+ m: timeFrame,
74
+ rc: rawCount,
75
+ });
76
+ });
77
+ return { pf: pf };
78
+ });
79
+ };
45
80
  return ImpressionCountsCacheInRedis;
46
81
  }(ImpressionCountsCacheInMemory));
47
82
  export { 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._toJSON(impressions)).then(function (queuedCount) {
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,6 +1,10 @@
1
1
  import { findLatencyIndex } from '../findLatencyIndex';
2
2
  import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter';
3
3
  import { CONSUMER_MODE, STORAGE_REDIS } from '../../utils/constants';
4
+ import { isNaNNumber, isString } from '../../utils/lang';
5
+ import { _Map } from '../../utils/lang/maps';
6
+ import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory';
7
+ import { parseLatencyField, parseExceptionField, parseMetadata } from '../utils';
4
8
  var TelemetryCacheInRedis = /** @class */ (function () {
5
9
  /**
6
10
  * Create a Telemetry cache that uses Redis as storage.
@@ -28,6 +32,102 @@ var TelemetryCacheInRedis = /** @class */ (function () {
28
32
  var value = JSON.stringify(getTelemetryConfigStats(CONSUMER_MODE, STORAGE_REDIS));
29
33
  return this.redis.hset(key, field, value).catch(function () { });
30
34
  };
35
+ /**
36
+ * Pop telemetry latencies.
37
+ * The returned promise rejects if redis operations fail.
38
+ */
39
+ TelemetryCacheInRedis.prototype.popLatencies = function () {
40
+ var _this = this;
41
+ return this.redis.hgetall(this.keys.latencyPrefix).then(function (latencies) {
42
+ var result = new _Map();
43
+ Object.keys(latencies).forEach(function (field) {
44
+ var parsedField = parseLatencyField(field);
45
+ if (isString(parsedField)) {
46
+ _this.log.error("Ignoring invalid latency field: " + field + ": " + parsedField);
47
+ return;
48
+ }
49
+ var count = parseInt(latencies[field]);
50
+ if (isNaNNumber(count)) {
51
+ _this.log.error("Ignoring latency with invalid count: " + latencies[field]);
52
+ return;
53
+ }
54
+ var metadata = parsedField[0], method = parsedField[1], bucket = parsedField[2];
55
+ if (bucket >= MAX_LATENCY_BUCKET_COUNT) {
56
+ _this.log.error("Ignoring latency with invalid bucket: " + bucket);
57
+ return;
58
+ }
59
+ if (!result.has(metadata))
60
+ result.set(metadata, {
61
+ t: newBuckets(),
62
+ ts: newBuckets(),
63
+ tc: newBuckets(),
64
+ tcs: newBuckets(),
65
+ tr: newBuckets(),
66
+ });
67
+ result.get(metadata)[method][bucket] = count;
68
+ });
69
+ return _this.redis.del(_this.keys.latencyPrefix).then(function () { return result; });
70
+ });
71
+ };
72
+ /**
73
+ * Pop telemetry exceptions.
74
+ * The returned promise rejects if redis operations fail.
75
+ */
76
+ TelemetryCacheInRedis.prototype.popExceptions = function () {
77
+ var _this = this;
78
+ return this.redis.hgetall(this.keys.exceptionPrefix).then(function (exceptions) {
79
+ var result = new _Map();
80
+ Object.keys(exceptions).forEach(function (field) {
81
+ var parsedField = parseExceptionField(field);
82
+ if (isString(parsedField)) {
83
+ _this.log.error("Ignoring invalid exception field: " + field + ": " + parsedField);
84
+ return;
85
+ }
86
+ var count = parseInt(exceptions[field]);
87
+ if (isNaNNumber(count)) {
88
+ _this.log.error("Ignoring exception with invalid count: " + exceptions[field]);
89
+ return;
90
+ }
91
+ var metadata = parsedField[0], method = parsedField[1];
92
+ if (!result.has(metadata))
93
+ result.set(metadata, {
94
+ t: 0,
95
+ ts: 0,
96
+ tc: 0,
97
+ tcs: 0,
98
+ tr: 0,
99
+ });
100
+ result.get(metadata)[method] = count;
101
+ });
102
+ return _this.redis.del(_this.keys.exceptionPrefix).then(function () { return result; });
103
+ });
104
+ };
105
+ /**
106
+ * Pop telemetry configs.
107
+ * The returned promise rejects if redis operations fail.
108
+ */
109
+ TelemetryCacheInRedis.prototype.popConfigs = function () {
110
+ var _this = this;
111
+ return this.redis.hgetall(this.keys.initPrefix).then(function (configs) {
112
+ var result = new _Map();
113
+ Object.keys(configs).forEach(function (field) {
114
+ var parsedField = parseMetadata(field);
115
+ if (isString(parsedField)) {
116
+ _this.log.error("Ignoring invalid config field: " + field + ": " + parsedField);
117
+ return;
118
+ }
119
+ var metadata = parsedField[0];
120
+ try {
121
+ var config = JSON.parse(configs[field]);
122
+ result.set(metadata, config);
123
+ }
124
+ catch (e) {
125
+ _this.log.error("Ignoring invalid config: " + configs[field]);
126
+ }
127
+ });
128
+ return _this.redis.del(_this.keys.initPrefix).then(function () { return result; });
129
+ });
130
+ };
31
131
  return TelemetryCacheInRedis;
32
132
  }());
33
133
  export { TelemetryCacheInRedis };
@@ -1,5 +1,5 @@
1
1
  import { __extends } from "tslib";
2
- import { UniqueKeysCacheInMemory } from '../inMemory/uniqueKeysCacheInMemory';
2
+ import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
3
3
  import { setToArray } from '../../utils/lang/sets';
4
4
  import { DEFAULT_CACHE_SIZE, REFRESH_RATE, TTL_REFRESH } from './constants';
5
5
  import { LOG_PREFIX } from './constants';
@@ -19,7 +19,7 @@ var UniqueKeysCacheInRedis = /** @class */ (function (_super) {
19
19
  UniqueKeysCacheInRedis.prototype.postUniqueKeysInRedis = function () {
20
20
  var _this = this;
21
21
  var featureNames = Object.keys(this.uniqueKeysTracker);
22
- if (!featureNames)
22
+ if (!featureNames.length)
23
23
  return Promise.resolve(false);
24
24
  var pipeline = this.redis.pipeline();
25
25
  for (var i = 0; i < featureNames.length; i++) {
@@ -41,7 +41,7 @@ var UniqueKeysCacheInRedis = /** @class */ (function (_super) {
41
41
  })
42
42
  .catch(function (err) {
43
43
  _this.log.error(LOG_PREFIX + "Error in uniqueKeys pipeline: " + err + ".");
44
- return Promise.resolve(false);
44
+ return false;
45
45
  });
46
46
  };
47
47
  UniqueKeysCacheInRedis.prototype.start = function () {
@@ -51,6 +51,18 @@ var UniqueKeysCacheInRedis = /** @class */ (function (_super) {
51
51
  clearInterval(this.intervalId);
52
52
  return this.postUniqueKeysInRedis();
53
53
  };
54
+ /**
55
+ * Async consumer API, used by synchronizer.
56
+ * @param count number of items to pop from the queue. If not provided or equal 0, all items will be popped.
57
+ */
58
+ UniqueKeysCacheInRedis.prototype.popNRaw = function (count) {
59
+ var _this = this;
60
+ if (count === void 0) { count = 0; }
61
+ return this.redis.lrange(this.key, 0, count - 1).then(function (uniqueKeyItems) {
62
+ return _this.redis.ltrim(_this.key, uniqueKeyItems.length, -1)
63
+ .then(function () { return uniqueKeyItems.map(function (uniqueKeyItem) { return JSON.parse(uniqueKeyItem); }); });
64
+ });
65
+ };
54
66
  return UniqueKeysCacheInRedis;
55
67
  }(UniqueKeysCacheInMemory));
56
68
  export { UniqueKeysCacheInRedis };
@@ -7,8 +7,9 @@ import { ImpressionsCacheInRedis } from './ImpressionsCacheInRedis';
7
7
  import { EventsCacheInRedis } from './EventsCacheInRedis';
8
8
  import { DEBUG, NONE, STORAGE_REDIS } from '../../utils/constants';
9
9
  import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
10
- import { UniqueKeysCacheInRedis } from './uniqueKeysCacheInRedis';
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(_a) {
20
- var log = _a.log, metadata = _a.metadata, onReadyCb = _a.onReadyCb, impressionsMode = _a.impressionsMode;
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);