@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
@@ -7,10 +7,10 @@ export class AttributesCacheInMemory {
7
7
 
8
8
  /**
9
9
  * Create or update the value for the given attribute
10
- *
10
+ *
11
11
  * @param {string} attributeName attribute name
12
12
  * @param {Object} attributeValue attribute value
13
- * @returns {boolean} the attribute was stored
13
+ * @returns {boolean} the attribute was stored
14
14
  */
15
15
  setAttribute(attributeName: string, attributeValue: Object): boolean {
16
16
  this.attributesCache[attributeName] = attributeValue;
@@ -19,7 +19,7 @@ export class AttributesCacheInMemory {
19
19
 
20
20
  /**
21
21
  * Retrieves the value of a given attribute
22
- *
22
+ *
23
23
  * @param {string} attributeName attribute name
24
24
  * @returns {Object?} stored attribute value
25
25
  */
@@ -29,7 +29,7 @@ export class AttributesCacheInMemory {
29
29
 
30
30
  /**
31
31
  * Create or update all the given attributes
32
- *
32
+ *
33
33
  * @param {[string, Object]} attributes attributes to create or update
34
34
  * @returns {boolean} attributes were stored
35
35
  */
@@ -40,7 +40,7 @@ export class AttributesCacheInMemory {
40
40
 
41
41
  /**
42
42
  * Retrieve the full attributes map
43
- *
43
+ *
44
44
  * @returns {Map<string, Object>} stored attributes
45
45
  */
46
46
  getAll(): Record<string, Object> {
@@ -49,7 +49,7 @@ export class AttributesCacheInMemory {
49
49
 
50
50
  /**
51
51
  * Removes a given attribute from the map
52
- *
52
+ *
53
53
  * @param {string} attributeName attribute to remove
54
54
  * @returns {boolean} attribute removed
55
55
  */
@@ -63,7 +63,7 @@ export class AttributesCacheInMemory {
63
63
 
64
64
  /**
65
65
  * Clears all attributes stored in the SDK
66
- *
66
+ *
67
67
  */
68
68
  clear() {
69
69
  this.attributesCache = {};
@@ -7,8 +7,8 @@ export class ImpressionCountsCacheInMemory implements IImpressionCountsCacheSync
7
7
  private readonly maxStorage: number;
8
8
  protected onFullQueue?: () => void;
9
9
  private cacheSize = 0;
10
-
11
- constructor(impressionCountsCacheSize: number = DEFAULT_CACHE_SIZE) {
10
+
11
+ constructor(impressionCountsCacheSize = DEFAULT_CACHE_SIZE) {
12
12
  this.maxStorage = impressionCountsCacheSize;
13
13
  }
14
14
 
@@ -4,9 +4,9 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
4
4
  import { EventsCacheInMemory } from './EventsCacheInMemory';
5
5
  import { IStorageFactoryParams, IStorageSync } from '../types';
6
6
  import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
- import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
8
- import { TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
- import { UniqueKeysCacheInMemory } from './uniqueKeysCacheInMemory';
7
+ import { DEBUG, NONE, STORAGE_MEMORY } from '../../utils/constants';
8
+ import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
+ import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
10
10
 
11
11
  /**
12
12
  * InMemory storage factory for standalone server-side SplitFactory
@@ -14,15 +14,19 @@ import { UniqueKeysCacheInMemory } from './uniqueKeysCacheInMemory';
14
14
  * @param params parameters required by EventsCacheSync
15
15
  */
16
16
  export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageSync {
17
+ const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode } } } = params;
18
+
19
+ const splits = new SplitsCacheInMemory();
20
+ const segments = new SegmentsCacheInMemory();
17
21
 
18
22
  return {
19
- splits: new SplitsCacheInMemory(),
20
- segments: new SegmentsCacheInMemory(),
21
- impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
22
- impressionCounts: params.impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
23
- events: new EventsCacheInMemory(params.eventsQueueSize),
24
- telemetry: params.mode !== LOCALHOST_MODE ? new TelemetryCacheInMemory() : undefined, // Always track telemetry in standalone mode on server-side
25
- uniqueKeys: params.impressionsMode === NONE ? new UniqueKeysCacheInMemory(params.uniqueKeysCacheSize) : undefined,
23
+ splits,
24
+ segments,
25
+ impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
26
+ impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
27
+ events: new EventsCacheInMemory(eventsQueueSize),
28
+ telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
29
+ uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemory() : undefined,
26
30
 
27
31
  // When using MEMORY we should clean all the caches to leave them empty
28
32
  destroy() {
@@ -4,9 +4,9 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
4
4
  import { EventsCacheInMemory } from './EventsCacheInMemory';
5
5
  import { IStorageSync, IStorageFactoryParams } from '../types';
6
6
  import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
- import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
7
+ import { DEBUG, NONE, STORAGE_MEMORY } from '../../utils/constants';
8
8
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
- import { UniqueKeysCacheInMemoryCS } from './uniqueKeysCacheInMemoryCS';
9
+ import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
10
10
 
11
11
  /**
12
12
  * InMemory storage factory for standalone client-side SplitFactory
@@ -14,16 +14,19 @@ import { UniqueKeysCacheInMemoryCS } from './uniqueKeysCacheInMemoryCS';
14
14
  * @param params parameters required by EventsCacheSync
15
15
  */
16
16
  export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSync {
17
+ const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode } } } = params;
18
+
19
+ const splits = new SplitsCacheInMemory();
20
+ const segments = new MySegmentsCacheInMemory();
17
21
 
18
22
  return {
19
- splits: new SplitsCacheInMemory(),
20
- segments: new MySegmentsCacheInMemory(),
21
- impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
22
- impressionCounts: params.impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
23
- events: new EventsCacheInMemory(params.eventsQueueSize),
24
- telemetry: params.mode !== LOCALHOST_MODE && shouldRecordTelemetry() ? new TelemetryCacheInMemory() : undefined,
25
- uniqueKeys: params.impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS(params.uniqueKeysCacheSize) : undefined,
26
-
23
+ splits,
24
+ segments,
25
+ impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
26
+ impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
27
+ events: new EventsCacheInMemory(eventsQueueSize),
28
+ telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
29
+ uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
27
30
 
28
31
  // When using MEMORY we should clean all the caches to leave them empty
29
32
  destroy() {
@@ -1,26 +1,66 @@
1
- import { ImpressionDataType, EventDataType, LastSync, HttpErrors, HttpLatencies, StreamingEvent, Method, OperationType, MethodExceptions, MethodLatencies } from '../../sync/submitters/types';
1
+ import { ImpressionDataType, EventDataType, LastSync, HttpErrors, HttpLatencies, StreamingEvent, Method, OperationType, MethodExceptions, MethodLatencies, TelemetryUsageStatsPayload } from '../../sync/submitters/types';
2
+ import { DEDUPED, DROPPED, LOCALHOST_MODE, QUEUED } from '../../utils/constants';
2
3
  import { findLatencyIndex } from '../findLatencyIndex';
3
- import { ITelemetryCacheSync } from '../types';
4
+ import { ISegmentsCacheSync, ISplitsCacheSync, IStorageFactoryParams, ITelemetryCacheSync } from '../types';
4
5
 
5
6
  const MAX_STREAMING_EVENTS = 20;
6
7
  const MAX_TAGS = 10;
8
+ export const MAX_LATENCY_BUCKET_COUNT = 23;
7
9
 
8
- function newBuckets() {
9
- // MAX_LATENCY_BUCKET_COUNT (length) is 23;
10
+ export function newBuckets() {
11
+ // MAX_LATENCY_BUCKET_COUNT (length) is 23
12
+ // Not using Array.fill for old browsers compatibility
10
13
  return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
11
14
  }
12
15
 
13
16
  const ACCEPTANCE_RANGE = 0.001;
14
17
 
15
18
  /**
16
- * Used on client-side. 0.1% of instances will track telemetry
19
+ * Record telemetry if mode is not localhost.
20
+ * All factory instances track telemetry on server-side, and 0.1% on client-side.
17
21
  */
18
- export function shouldRecordTelemetry() {
19
- return Math.random() <= ACCEPTANCE_RANGE;
22
+ export function shouldRecordTelemetry({ settings }: IStorageFactoryParams) {
23
+ return settings.mode !== LOCALHOST_MODE && (settings.core.key === undefined || Math.random() <= ACCEPTANCE_RANGE);
20
24
  }
21
25
 
22
26
  export class TelemetryCacheInMemory implements ITelemetryCacheSync {
23
27
 
28
+ constructor(private splits?: ISplitsCacheSync, private segments?: ISegmentsCacheSync) { }
29
+
30
+ // isEmpty flag
31
+ private e = true;
32
+
33
+ isEmpty() { return this.e; }
34
+
35
+ clear() { /* no-op */ }
36
+
37
+ pop(): TelemetryUsageStatsPayload {
38
+ this.e = true;
39
+
40
+ return {
41
+ lS: this.getLastSynchronization(),
42
+ mL: this.popLatencies(),
43
+ mE: this.popExceptions(),
44
+ hE: this.popHttpErrors(),
45
+ hL: this.popHttpLatencies(),
46
+ tR: this.popTokenRefreshes(),
47
+ aR: this.popAuthRejections(),
48
+ iQ: this.getImpressionStats(QUEUED),
49
+ iDe: this.getImpressionStats(DEDUPED),
50
+ iDr: this.getImpressionStats(DROPPED),
51
+ spC: this.splits && this.splits.getSplitNames().length,
52
+ seC: this.segments && this.segments.getRegisteredSegments().length,
53
+ skC: this.segments && this.segments.getKeysCount(),
54
+ sL: this.getSessionLength(),
55
+ eQ: this.getEventStats(QUEUED),
56
+ eD: this.getEventStats(DROPPED),
57
+ sE: this.popStreamingEvents(),
58
+ t: this.popTags(),
59
+ };
60
+ }
61
+
62
+ /** Config stats */
63
+
24
64
  private timeUntilReady?: number;
25
65
 
26
66
  getTimeUntilReady() {
@@ -51,6 +91,8 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
51
91
  this.notReadyUsage++;
52
92
  }
53
93
 
94
+ /** Usage stats */
95
+
54
96
  private impressionStats = [0, 0, 0];
55
97
 
56
98
  getImpressionStats(type: ImpressionDataType) {
@@ -59,6 +101,7 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
59
101
 
60
102
  recordImpressionStats(type: ImpressionDataType, count: number) {
61
103
  this.impressionStats[type] += count;
104
+ this.e = false;
62
105
  }
63
106
 
64
107
  private eventStats = [0, 0];
@@ -69,9 +112,9 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
69
112
 
70
113
  recordEventStats(type: EventDataType, count: number) {
71
114
  this.eventStats[type] += count;
115
+ this.e = false;
72
116
  }
73
117
 
74
- // @ts-expect-error
75
118
  private lastSync: LastSync = {};
76
119
 
77
120
  getLastSynchronization() {
@@ -80,40 +123,35 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
80
123
 
81
124
  recordSuccessfulSync(resource: OperationType, timeMs: number) {
82
125
  this.lastSync[resource] = timeMs;
126
+ this.e = false;
83
127
  }
84
128
 
85
- // @ts-expect-error
86
129
  private httpErrors: HttpErrors = {};
87
130
 
88
131
  popHttpErrors() {
89
- const result = this.httpErrors; // @ts-expect-error
132
+ const result = this.httpErrors;
90
133
  this.httpErrors = {};
91
134
  return result;
92
135
  }
93
136
 
94
137
  recordHttpError(resource: OperationType, status: number) {
95
- if (!this.httpErrors[resource]) this.httpErrors[resource] = {};
96
- if (!this.httpErrors[resource][status]) {
97
- this.httpErrors[resource][status] = 1;
98
- } else {
99
- this.httpErrors[resource][status]++;
100
- }
138
+ const statusErrors = (this.httpErrors[resource] = this.httpErrors[resource] || {});
139
+ statusErrors[status] = (statusErrors[status] || 0) + 1;
140
+ this.e = false;
101
141
  }
102
142
 
103
- // @ts-expect-error
104
143
  private httpLatencies: HttpLatencies = {};
105
144
 
106
145
  popHttpLatencies() {
107
- const result = this.httpLatencies; // @ts-expect-error
146
+ const result = this.httpLatencies;
108
147
  this.httpLatencies = {};
109
148
  return result;
110
149
  }
111
150
 
112
151
  recordHttpLatency(resource: OperationType, latencyMs: number) {
113
- if (!this.httpLatencies[resource]) {
114
- this.httpLatencies[resource] = newBuckets();
115
- }
116
- this.httpLatencies[resource][findLatencyIndex(latencyMs)]++;
152
+ const latencyBuckets = (this.httpLatencies[resource] = this.httpLatencies[resource] || newBuckets());
153
+ latencyBuckets[findLatencyIndex(latencyMs)]++;
154
+ this.e = false;
117
155
  }
118
156
 
119
157
  private authRejections = 0;
@@ -126,6 +164,7 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
126
164
 
127
165
  recordAuthRejections() {
128
166
  this.authRejections++;
167
+ this.e = false;
129
168
  }
130
169
 
131
170
  private tokenRefreshes = 0;
@@ -138,6 +177,7 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
138
177
 
139
178
  recordTokenRefreshes() {
140
179
  this.tokenRefreshes++;
180
+ this.e = false;
141
181
  }
142
182
 
143
183
  private streamingEvents: StreamingEvent[] = []
@@ -150,6 +190,7 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
150
190
  if (this.streamingEvents.length < MAX_STREAMING_EVENTS) {
151
191
  this.streamingEvents.push(streamingEvent);
152
192
  }
193
+ this.e = false;
153
194
  }
154
195
 
155
196
  private tags: string[] = [];
@@ -162,6 +203,7 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
162
203
  if (this.tags.length < MAX_TAGS) {
163
204
  this.tags.push(tag);
164
205
  }
206
+ this.e = false;
165
207
  }
166
208
 
167
209
  private sessionLength?: number;
@@ -172,39 +214,34 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
172
214
 
173
215
  recordSessionLength(ms: number) {
174
216
  this.sessionLength = ms;
217
+ this.e = false;
175
218
  }
176
219
 
177
- // @ts-expect-error
178
220
  private exceptions: MethodExceptions = {};
179
221
 
180
222
  popExceptions() {
181
- const result = this.exceptions; // @ts-expect-error
223
+ const result = this.exceptions;
182
224
  this.exceptions = {};
183
225
  return result;
184
226
  }
185
227
 
186
228
  recordException(method: Method) {
187
- if (!this.exceptions[method]) {
188
- this.exceptions[method] = 1;
189
- } else {
190
- this.exceptions[method]++;
191
- }
229
+ this.exceptions[method] = (this.exceptions[method] || 0) + 1;
230
+ this.e = false;
192
231
  }
193
232
 
194
- // @ts-expect-error
195
233
  private latencies: MethodLatencies = {};
196
234
 
197
235
  popLatencies() {
198
- const result = this.latencies; // @ts-expect-error
236
+ const result = this.latencies;
199
237
  this.latencies = {};
200
238
  return result;
201
239
  }
202
240
 
203
241
  recordLatency(method: Method, latencyMs: number) {
204
- if (!this.latencies[method]) {
205
- this.latencies[method] = newBuckets();
206
- }
207
- this.latencies[method][findLatencyIndex(latencyMs)]++;
242
+ const latencyBuckets = (this.latencies[method] = this.latencies[method] || newBuckets());
243
+ latencyBuckets[findLatencyIndex(latencyMs)]++;
244
+ this.e = false;
208
245
  }
209
246
 
210
247
  }
@@ -3,14 +3,33 @@ import { ISet, setToArray, _Set } from '../../utils/lang/sets';
3
3
  import { UniqueKeysPayloadSs } from '../../sync/submitters/types';
4
4
  import { DEFAULT_CACHE_SIZE } from '../inRedis/constants';
5
5
 
6
+ /**
7
+ * Converts `uniqueKeys` data from cache into request payload for SS.
8
+ */
9
+ export function fromUniqueKeysCollector(uniqueKeys: { [featureName: string]: ISet<string> }): UniqueKeysPayloadSs {
10
+ const payload = [];
11
+ const featureNames = Object.keys(uniqueKeys);
12
+ for (let i = 0; i < featureNames.length; i++) {
13
+ const featureName = featureNames[i];
14
+ const userKeys = setToArray(uniqueKeys[featureName]);
15
+ const uniqueKeysPayload = {
16
+ f: featureName,
17
+ ks: userKeys
18
+ };
19
+
20
+ payload.push(uniqueKeysPayload);
21
+ }
22
+ return { keys: payload };
23
+ }
24
+
6
25
  export class UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
7
26
 
8
27
  protected onFullQueue?: () => void;
9
28
  private readonly maxStorage: number;
10
29
  private uniqueTrackerSize = 0;
11
- protected uniqueKeysTracker: { [keys: string]: ISet<string> };
30
+ protected uniqueKeysTracker: { [featureName: string]: ISet<string> };
12
31
 
13
- constructor(uniqueKeysQueueSize: number = DEFAULT_CACHE_SIZE) {
32
+ constructor(uniqueKeysQueueSize = DEFAULT_CACHE_SIZE) {
14
33
  this.maxStorage = uniqueKeysQueueSize;
15
34
  this.uniqueKeysTracker = {};
16
35
  }
@@ -20,15 +39,13 @@ export class UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
20
39
  }
21
40
 
22
41
  /**
23
- * Store unique keys in sequential order
24
- * key: string = feature name.
25
- * value: Set<string> = set of unique keys.
42
+ * Store unique keys per feature.
26
43
  */
27
- track(key: string, featureName: string) {
44
+ track(userKey: string, featureName: string) {
28
45
  if (!this.uniqueKeysTracker[featureName]) this.uniqueKeysTracker[featureName] = new _Set();
29
46
  const tracker = this.uniqueKeysTracker[featureName];
30
- if (!tracker.has(key)) {
31
- tracker.add(key);
47
+ if (!tracker.has(userKey)) {
48
+ tracker.add(userKey);
32
49
  this.uniqueTrackerSize++;
33
50
  }
34
51
  if (this.uniqueTrackerSize >= this.maxStorage && this.onFullQueue) {
@@ -50,7 +67,7 @@ export class UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
50
67
  pop() {
51
68
  const data = this.uniqueKeysTracker;
52
69
  this.uniqueKeysTracker = {};
53
- return this.fromUniqueKeysCollector(data);
70
+ return fromUniqueKeysCollector(data);
54
71
  }
55
72
 
56
73
  /**
@@ -59,24 +76,5 @@ export class UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
59
76
  isEmpty() {
60
77
  return Object.keys(this.uniqueKeysTracker).length === 0;
61
78
  }
62
-
63
- /**
64
- * Converts `uniqueKeys` data from cache into request payload for SS.
65
- */
66
- private fromUniqueKeysCollector(uniqueKeys: { [featureName: string]: ISet<string> }): UniqueKeysPayloadSs {
67
- const payload = [];
68
- const featureNames = Object.keys(uniqueKeys);
69
- for (let i = 0; i < featureNames.length; i++) {
70
- const featureName = featureNames[i];
71
- const featureKeys = setToArray(uniqueKeys[featureName]);
72
- const uniqueKeysPayload = {
73
- f: featureName,
74
- ks: featureKeys
75
- };
76
79
 
77
- payload.push(uniqueKeysPayload);
78
- }
79
- return { keys: payload };
80
- }
81
-
82
80
  }
@@ -8,14 +8,14 @@ export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheBase {
8
8
  private onFullQueue?: () => void;
9
9
  private readonly maxStorage: number;
10
10
  private uniqueTrackerSize = 0;
11
- private uniqueKeysTracker: { [keys: string]: ISet<string> };
11
+ private uniqueKeysTracker: { [userKey: string]: ISet<string> };
12
12
 
13
13
  /**
14
14
  *
15
15
  * @param impressionsQueueSize number of queued impressions to call onFullQueueCb.
16
16
  * Default value is 0, that means no maximum value, in case we want to avoid this being triggered.
17
17
  */
18
- constructor(uniqueKeysQueueSize: number = DEFAULT_CACHE_SIZE) {
18
+ constructor(uniqueKeysQueueSize = DEFAULT_CACHE_SIZE) {
19
19
  this.maxStorage = uniqueKeysQueueSize;
20
20
  this.uniqueKeysTracker = {};
21
21
  }
@@ -25,14 +25,12 @@ export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheBase {
25
25
  }
26
26
 
27
27
  /**
28
- * Store unique keys in sequential order
29
- * key: string = key.
30
- * value: HashSet<string> = set of split names.
28
+ * Store unique keys per feature.
31
29
  */
32
- track(key: string, featureName: string) {
33
-
34
- if (!this.uniqueKeysTracker[key]) this.uniqueKeysTracker[key] = new _Set();
35
- const tracker = this.uniqueKeysTracker[key];
30
+ track(userKey: string, featureName: string) {
31
+
32
+ if (!this.uniqueKeysTracker[userKey]) this.uniqueKeysTracker[userKey] = new _Set();
33
+ const tracker = this.uniqueKeysTracker[userKey];
36
34
  if (!tracker.has(featureName)) {
37
35
  tracker.add(featureName);
38
36
  this.uniqueTrackerSize++;
@@ -65,18 +63,18 @@ export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheBase {
65
63
  isEmpty() {
66
64
  return Object.keys(this.uniqueKeysTracker).length === 0;
67
65
  }
68
-
66
+
69
67
  /**
70
68
  * Converts `uniqueKeys` data from cache into request payload.
71
69
  */
72
- private fromUniqueKeysCollector(uniqueKeys: { [featureName: string]: ISet<string> }): UniqueKeysPayloadCs {
70
+ private fromUniqueKeysCollector(uniqueKeys: { [userKey: string]: ISet<string> }): UniqueKeysPayloadCs {
73
71
  const payload = [];
74
- const featureKeys = Object.keys(uniqueKeys);
75
- for (let k = 0; k < featureKeys.length; k++) {
76
- const featureKey = featureKeys[k];
77
- const featureNames = setToArray(uniqueKeys[featureKey]);
72
+ const userKeys = Object.keys(uniqueKeys);
73
+ for (let k = 0; k < userKeys.length; k++) {
74
+ const userKey = userKeys[k];
75
+ const featureNames = setToArray(uniqueKeys[userKey]);
78
76
  const uniqueKeysPayload = {
79
- k: featureKey,
77
+ k: userKey,
80
78
  fs: featureNames
81
79
  };
82
80
 
@@ -84,5 +82,5 @@ export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheBase {
84
82
  }
85
83
  return { keys: payload };
86
84
  }
87
-
85
+
88
86
  }
@@ -59,7 +59,7 @@ export class EventsCacheInRedis implements IEventsCacheAsync {
59
59
 
60
60
  /**
61
61
  * Pop the given number of events from the storage.
62
- * The returned promise rejects if the wrapper operation fails.
62
+ * The returned promise rejects if the redis operation fails.
63
63
  *
64
64
  * NOTE: this method doesn't take into account MAX_EVENT_SIZE or MAX_QUEUE_BYTE_SIZE limits.
65
65
  * It is the submitter responsability to handle that.
@@ -1,5 +1,7 @@
1
1
  import { Redis } from 'ioredis';
2
2
  import { ILogger } from '../../logger/types';
3
+ import { ImpressionCountsPayload } from '../../sync/submitters/types';
4
+ import { forOwn } from '../../utils/lang';
3
5
  import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
4
6
  import { LOG_PREFIX, REFRESH_RATE, TTL_REFRESH } from './constants';
5
7
 
@@ -10,8 +12,8 @@ export class ImpressionCountsCacheInRedis extends ImpressionCountsCacheInMemory
10
12
  private readonly redis: Redis;
11
13
  private readonly refreshRate: number;
12
14
  private intervalId: any;
13
-
14
- constructor(log: ILogger, key: string, redis: Redis, impressionCountsCacheSize?: number, refreshRate: number = REFRESH_RATE) {
15
+
16
+ constructor(log: ILogger, key: string, redis: Redis, impressionCountsCacheSize?: number, refreshRate = REFRESH_RATE) {
15
17
  super(impressionCountsCacheSize);
16
18
  this.log = log;
17
19
  this.key = key;
@@ -19,11 +21,12 @@ export class ImpressionCountsCacheInRedis extends ImpressionCountsCacheInMemory
19
21
  this.refreshRate = refreshRate;
20
22
  this.onFullQueue = () => { this.postImpressionCountsInRedis(); };
21
23
  }
22
-
23
- postImpressionCountsInRedis(){
24
+
25
+ private postImpressionCountsInRedis() {
24
26
  const counts = this.pop();
25
27
  const keys = Object.keys(counts);
26
- if (!keys) return Promise.resolve(false);
28
+ if (!keys.length) return Promise.resolve(false);
29
+
27
30
  const pipeline = this.redis.pipeline();
28
31
  keys.forEach(key => {
29
32
  pipeline.hincrby(this.key, key, counts[key]);
@@ -37,16 +40,56 @@ export class ImpressionCountsCacheInRedis extends ImpressionCountsCacheInMemory
37
40
  })
38
41
  .catch(err => {
39
42
  this.log.error(`${LOG_PREFIX}Error in impression counts pipeline: ${err}.`);
40
- return Promise.resolve(false);
43
+ return false;
41
44
  });
42
45
  }
43
-
46
+
44
47
  start() {
45
48
  this.intervalId = setInterval(this.postImpressionCountsInRedis.bind(this), this.refreshRate);
46
49
  }
47
-
50
+
48
51
  stop() {
49
52
  clearInterval(this.intervalId);
50
53
  return this.postImpressionCountsInRedis();
51
54
  }
55
+
56
+ // Async consumer API, used by synchronizer
57
+ getImpressionsCount(): Promise<ImpressionCountsPayload | undefined> {
58
+ return this.redis.hgetall(this.key)
59
+ .then(counts => {
60
+ if (!Object.keys(counts).length) return undefined;
61
+
62
+ this.redis.del(this.key).catch(() => { /* no-op */ });
63
+
64
+ const pf: ImpressionCountsPayload['pf'] = [];
65
+
66
+ forOwn(counts, (count, key) => {
67
+ const nameAndTime = key.split('::');
68
+ if (nameAndTime.length !== 2) {
69
+ this.log.error(`${LOG_PREFIX}Error spliting key ${key}`);
70
+ return;
71
+ }
72
+
73
+ const timeFrame = parseInt(nameAndTime[1]);
74
+ if (isNaN(timeFrame)) {
75
+ this.log.error(`${LOG_PREFIX}Error parsing time frame ${nameAndTime[1]}`);
76
+ return;
77
+ }
78
+
79
+ const rawCount = parseInt(count);
80
+ if (isNaN(rawCount)) {
81
+ this.log.error(`${LOG_PREFIX}Error parsing raw count ${count}`);
82
+ return;
83
+ }
84
+
85
+ pf.push({
86
+ f: nameAndTime[0],
87
+ m: timeFrame,
88
+ rc: rawCount,
89
+ });
90
+ });
91
+
92
+ return { pf };
93
+ });
94
+ }
52
95
  }