@splitsoftware/splitio-commons 1.6.2-rc.1 → 1.6.2-rc.10

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 (197) hide show
  1. package/CHANGES.txt +4 -0
  2. package/cjs/consent/sdkUserConsent.js +2 -2
  3. package/cjs/evaluator/index.js +15 -16
  4. package/cjs/integrations/ga/GaToSplit.js +8 -5
  5. package/cjs/sdkClient/client.js +19 -7
  6. package/cjs/sdkClient/sdkClient.js +3 -1
  7. package/cjs/sdkFactory/index.js +15 -6
  8. package/cjs/sdkManager/index.js +3 -11
  9. package/cjs/services/splitApi.js +6 -6
  10. package/cjs/storages/AbstractSplitsCacheAsync.js +8 -10
  11. package/cjs/storages/AbstractSplitsCacheSync.js +8 -10
  12. package/cjs/storages/KeyBuilderSS.js +54 -9
  13. package/cjs/storages/dataLoader.js +1 -1
  14. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +5 -7
  15. package/cjs/storages/inLocalStorage/index.js +5 -1
  16. package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
  17. package/cjs/storages/inMemory/InMemoryStorage.js +6 -2
  18. package/cjs/storages/inMemory/InMemoryStorageCS.js +6 -2
  19. package/cjs/storages/inMemory/SplitsCacheInMemory.js +7 -10
  20. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +10 -5
  21. package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +73 -0
  22. package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +78 -0
  23. package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
  24. package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +50 -0
  25. package/cjs/storages/inRedis/SplitsCacheInRedis.js +15 -9
  26. package/cjs/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  27. package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +59 -0
  28. package/cjs/storages/inRedis/constants.js +4 -1
  29. package/cjs/storages/inRedis/index.js +17 -2
  30. package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +43 -0
  31. package/cjs/storages/pluggable/SplitsCachePluggable.js +14 -9
  32. package/cjs/storages/pluggable/TelemetryCachePluggable.js +126 -0
  33. package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +50 -0
  34. package/cjs/storages/pluggable/index.js +42 -17
  35. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  36. package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
  37. package/cjs/sync/submitters/telemetrySubmitter.js +8 -4
  38. package/cjs/sync/submitters/uniqueKeysSubmitter.js +16 -59
  39. package/cjs/trackers/impressionsTracker.js +17 -15
  40. package/cjs/trackers/strategy/strategyNone.js +1 -1
  41. package/cjs/trackers/strategy/strategyOptimized.js +2 -1
  42. package/cjs/trackers/telemetryTracker.js +6 -0
  43. package/cjs/trackers/uniqueKeysTracker.js +11 -42
  44. package/cjs/utils/constants/index.js +3 -2
  45. package/cjs/utils/lang/maps.js +15 -7
  46. package/cjs/utils/redis/RedisMock.js +31 -0
  47. package/cjs/utils/settingsValidation/index.js +0 -3
  48. package/esm/consent/sdkUserConsent.js +2 -2
  49. package/esm/evaluator/index.js +15 -16
  50. package/esm/integrations/ga/GaToSplit.js +8 -5
  51. package/esm/sdkClient/client.js +19 -7
  52. package/esm/sdkClient/sdkClient.js +3 -1
  53. package/esm/sdkFactory/index.js +16 -7
  54. package/esm/sdkManager/index.js +3 -11
  55. package/esm/services/splitApi.js +6 -6
  56. package/esm/storages/AbstractSplitsCacheAsync.js +8 -10
  57. package/esm/storages/AbstractSplitsCacheSync.js +8 -10
  58. package/esm/storages/KeyBuilderSS.js +50 -8
  59. package/esm/storages/dataLoader.js +1 -1
  60. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -7
  61. package/esm/storages/inLocalStorage/index.js +6 -2
  62. package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
  63. package/esm/storages/inMemory/InMemoryStorage.js +8 -4
  64. package/esm/storages/inMemory/InMemoryStorageCS.js +7 -3
  65. package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -10
  66. package/esm/storages/inMemory/TelemetryCacheInMemory.js +9 -5
  67. package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +70 -0
  68. package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +75 -0
  69. package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
  70. package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +47 -0
  71. package/esm/storages/inRedis/SplitsCacheInRedis.js +15 -9
  72. package/esm/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  73. package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +56 -0
  74. package/esm/storages/inRedis/constants.js +3 -0
  75. package/esm/storages/inRedis/index.js +18 -3
  76. package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +40 -0
  77. package/esm/storages/pluggable/SplitsCachePluggable.js +14 -9
  78. package/esm/storages/pluggable/TelemetryCachePluggable.js +126 -0
  79. package/esm/storages/pluggable/UniqueKeysCachePluggable.js +47 -0
  80. package/esm/storages/pluggable/index.js +43 -18
  81. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  82. package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
  83. package/esm/sync/submitters/telemetrySubmitter.js +9 -5
  84. package/esm/sync/submitters/uniqueKeysSubmitter.js +15 -56
  85. package/esm/trackers/impressionsTracker.js +17 -15
  86. package/esm/trackers/strategy/strategyNone.js +1 -1
  87. package/esm/trackers/strategy/strategyOptimized.js +2 -1
  88. package/esm/trackers/telemetryTracker.js +6 -0
  89. package/esm/trackers/uniqueKeysTracker.js +11 -42
  90. package/esm/utils/constants/index.js +1 -0
  91. package/esm/utils/lang/maps.js +15 -7
  92. package/esm/utils/redis/RedisMock.js +28 -0
  93. package/esm/utils/settingsValidation/index.js +0 -3
  94. package/package.json +1 -2
  95. package/src/consent/sdkUserConsent.ts +2 -2
  96. package/src/evaluator/index.ts +14 -15
  97. package/src/integrations/ga/GaToSplit.ts +9 -5
  98. package/src/integrations/types.ts +2 -1
  99. package/src/logger/.DS_Store +0 -0
  100. package/src/sdkClient/client.ts +21 -8
  101. package/src/sdkClient/sdkClient.ts +3 -1
  102. package/src/sdkFactory/index.ts +17 -7
  103. package/src/sdkManager/index.ts +3 -12
  104. package/src/services/splitApi.ts +6 -6
  105. package/src/services/types.ts +2 -2
  106. package/src/storages/AbstractSplitsCacheAsync.ts +13 -15
  107. package/src/storages/AbstractSplitsCacheSync.ts +15 -17
  108. package/src/storages/KeyBuilderSS.ts +61 -9
  109. package/src/storages/dataLoader.ts +1 -1
  110. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +8 -11
  111. package/src/storages/inLocalStorage/index.ts +5 -2
  112. package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +16 -1
  113. package/src/storages/inMemory/InMemoryStorage.ts +7 -4
  114. package/src/storages/inMemory/InMemoryStorageCS.ts +6 -3
  115. package/src/storages/inMemory/SplitsCacheInMemory.ts +10 -14
  116. package/src/storages/inMemory/TelemetryCacheInMemory.ts +10 -6
  117. package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +82 -0
  118. package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +88 -0
  119. package/src/storages/inRedis/EventsCacheInRedis.ts +1 -1
  120. package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +53 -0
  121. package/src/storages/inRedis/SplitsCacheInRedis.ts +21 -17
  122. package/src/storages/inRedis/TelemetryCacheInRedis.ts +122 -2
  123. package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +65 -0
  124. package/src/storages/inRedis/constants.ts +3 -0
  125. package/src/storages/inRedis/index.ts +15 -5
  126. package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +47 -0
  127. package/src/storages/pluggable/SplitsCachePluggable.ts +20 -17
  128. package/src/storages/pluggable/TelemetryCachePluggable.ts +147 -2
  129. package/src/storages/pluggable/UniqueKeysCachePluggable.ts +56 -0
  130. package/src/storages/pluggable/index.ts +44 -19
  131. package/src/storages/types.ts +38 -30
  132. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +5 -6
  133. package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -2
  134. package/src/sync/submitters/telemetrySubmitter.ts +15 -8
  135. package/src/sync/submitters/types.ts +18 -6
  136. package/src/sync/submitters/uniqueKeysSubmitter.ts +18 -61
  137. package/src/trackers/impressionsTracker.ts +16 -15
  138. package/src/trackers/strategy/strategyNone.ts +1 -1
  139. package/src/trackers/strategy/strategyOptimized.ts +1 -1
  140. package/src/trackers/telemetryTracker.ts +7 -2
  141. package/src/trackers/types.ts +9 -7
  142. package/src/trackers/uniqueKeysTracker.ts +15 -47
  143. package/src/types.ts +0 -1
  144. package/src/utils/constants/index.ts +1 -0
  145. package/src/utils/lang/maps.ts +20 -8
  146. package/src/utils/redis/RedisMock.ts +33 -0
  147. package/src/utils/settingsValidation/index.ts +1 -4
  148. package/types/integrations/types.d.ts +2 -1
  149. package/types/services/types.d.ts +2 -2
  150. package/types/storages/AbstractSplitsCacheAsync.d.ts +7 -6
  151. package/types/storages/AbstractSplitsCacheSync.d.ts +6 -6
  152. package/types/storages/KeyBuilderSS.d.ts +9 -2
  153. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +3 -4
  154. package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +5 -1
  155. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +3 -2
  156. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +6 -3
  157. package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +35 -0
  158. package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +37 -0
  159. package/types/storages/inRedis/EventsCacheInRedis.d.ts +1 -1
  160. package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +14 -0
  161. package/types/storages/inRedis/SplitsCacheInRedis.d.ts +6 -5
  162. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +16 -1
  163. package/types/storages/inRedis/constants.d.ts +3 -0
  164. package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +15 -0
  165. package/types/storages/pluggable/ImpressionCountsCachePluggable.d.ts +14 -0
  166. package/types/storages/pluggable/SplitsCachePluggable.d.ts +6 -5
  167. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +17 -1
  168. package/types/storages/pluggable/UniqueKeysCachePluggable.d.ts +14 -0
  169. package/types/storages/types.d.ts +35 -35
  170. package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
  171. package/types/sync/submitters/telemetrySubmitter.d.ts +1 -1
  172. package/types/sync/submitters/types.d.ts +12 -6
  173. package/types/sync/submitters/uniqueKeysSubmitter.d.ts +0 -14
  174. package/types/trackers/types.d.ts +9 -11
  175. package/types/trackers/uniqueKeysTracker.d.ts +3 -3
  176. package/types/types.d.ts +0 -1
  177. package/types/utils/constants/index.d.ts +1 -0
  178. package/types/utils/lang/maps.d.ts +6 -2
  179. package/types/utils/redis/RedisMock.d.ts +4 -0
  180. package/types/utils/settingsValidation/index.d.ts +0 -1
  181. package/types/sdkClient/types.d.ts +0 -18
  182. package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
  183. package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
  184. package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
  185. package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
  186. package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
  187. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
  188. package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
  189. package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
  190. package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
  191. package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
  192. package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
  193. package/types/sync/syncTaskComposite.d.ts +0 -5
  194. package/types/trackers/filter/bloomFilter.d.ts +0 -10
  195. package/types/trackers/filter/dictionaryFilter.d.ts +0 -8
  196. package/types/trackers/filter/types.d.ts +0 -5
  197. package/types/utils/timeTracker/index.d.ts +0 -70
@@ -0,0 +1,47 @@
1
+ import { ILogger } from '../../logger/types';
2
+ import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
3
+ import { REFRESH_RATE } from '../inRedis/constants';
4
+ import { IPluggableStorageWrapper } from '../types';
5
+ import { LOG_PREFIX } from './constants';
6
+
7
+ export class ImpressionCountsCachePluggable extends ImpressionCountsCacheInMemory {
8
+
9
+ private readonly log: ILogger;
10
+ private readonly key: string;
11
+ private readonly wrapper: IPluggableStorageWrapper;
12
+ private readonly refreshRate: number;
13
+ private intervalId: any;
14
+
15
+ constructor(log: ILogger, key: string, wrapper: IPluggableStorageWrapper, impressionCountsCacheSize?: number, refreshRate = REFRESH_RATE) {
16
+ super(impressionCountsCacheSize);
17
+ this.log = log;
18
+ this.key = key;
19
+ this.wrapper = wrapper;
20
+ this.refreshRate = refreshRate;
21
+ this.onFullQueue = () => { this.storeImpressionCounts(); };
22
+ }
23
+
24
+ private storeImpressionCounts() {
25
+ const counts = this.pop();
26
+ const keys = Object.keys(counts);
27
+ if (!keys.length) return Promise.resolve(false);
28
+
29
+ const pipeline = keys.reduce<Promise<any>>((pipeline, key) => {
30
+ return pipeline.then(() => this.wrapper.incr(`${this.key}::${key}`, counts[key]));
31
+ }, Promise.resolve());
32
+
33
+ return pipeline.catch(err => {
34
+ this.log.error(`${LOG_PREFIX}Error in impression counts pipeline: ${err}.`);
35
+ return false;
36
+ });
37
+ }
38
+
39
+ start() {
40
+ this.intervalId = setInterval(this.storeImpressionCounts.bind(this), this.refreshRate);
41
+ }
42
+
43
+ stop() {
44
+ clearInterval(this.intervalId);
45
+ return this.storeImpressionCounts();
46
+ }
47
+ }
@@ -49,22 +49,22 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
49
49
  * The returned promise is resolved when the operation success
50
50
  * or rejected if it fails (e.g., wrapper operation fails)
51
51
  */
52
- addSplit(name: string, split: string): Promise<boolean> {
52
+ addSplit(name: string, split: ISplit): Promise<boolean> {
53
53
  const splitKey = this.keys.buildSplitKey(name);
54
54
  return this.wrapper.get(splitKey).then(splitFromStorage => {
55
55
 
56
56
  // handling parsing error
57
- let parsedPreviousSplit, parsedSplit;
57
+ let parsedPreviousSplit, stringifiedNewSplit;
58
58
  try {
59
59
  parsedPreviousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : undefined;
60
- parsedSplit = JSON.parse(split);
60
+ stringifiedNewSplit = JSON.stringify(split);
61
61
  } catch (e) {
62
62
  throw new Error('Error parsing split definition: ' + e);
63
63
  }
64
64
 
65
65
  return Promise.all([
66
- this.wrapper.set(splitKey, split),
67
- this._incrementCounts(parsedSplit),
66
+ this.wrapper.set(splitKey, stringifiedNewSplit),
67
+ this._incrementCounts(split),
68
68
  // If it's an update, we decrement the traffic type and segment count of the existing split,
69
69
  parsedPreviousSplit && this._decrementCounts(parsedPreviousSplit)
70
70
  ]);
@@ -76,7 +76,7 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
76
76
  * The returned promise is resolved when the operation success
77
77
  * or rejected if it fails (e.g., wrapper operation fails)
78
78
  */
79
- addSplits(entries: [string, string][]): Promise<boolean[]> {
79
+ addSplits(entries: [string, ISplit][]): Promise<boolean[]> {
80
80
  return Promise.all(entries.map(keyValuePair => this.addSplit(keyValuePair[0], keyValuePair[1])));
81
81
  }
82
82
 
@@ -88,8 +88,7 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
88
88
  removeSplit(name: string) {
89
89
  return this.getSplit(name).then((split) => {
90
90
  if (split) {
91
- const parsedSplit = JSON.parse(split);
92
- this._decrementCounts(parsedSplit);
91
+ this._decrementCounts(split);
93
92
  }
94
93
  return this.wrapper.del(this.keys.buildSplitKey(name));
95
94
  });
@@ -109,8 +108,9 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
109
108
  * The returned promise is resolved with the split definition or null if it's not defined,
110
109
  * or rejected if wrapper operation fails.
111
110
  */
112
- getSplit(name: string): Promise<string | null> {
113
- return this.wrapper.get(this.keys.buildSplitKey(name));
111
+ getSplit(name: string): Promise<ISplit | null> {
112
+ return this.wrapper.get(this.keys.buildSplitKey(name))
113
+ .then(maybeSplit => maybeSplit && JSON.parse(maybeSplit));
114
114
  }
115
115
 
116
116
  /**
@@ -118,13 +118,14 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
118
118
  * The returned promise is resolved with a map of split names to their split definition or null if it's not defined,
119
119
  * or rejected if wrapper operation fails.
120
120
  */
121
- getSplits(names: string[]): Promise<Record<string, string | null>> {
121
+ getSplits(names: string[]): Promise<Record<string, ISplit | null>> {
122
122
  const keys = names.map(name => this.keys.buildSplitKey(name));
123
123
 
124
124
  return this.wrapper.getMany(keys).then(splitDefinitions => {
125
- const splits: Record<string, string | null> = {};
125
+ const splits: Record<string, ISplit | null> = {};
126
126
  names.forEach((name, idx) => {
127
- splits[name] = splitDefinitions[idx];
127
+ const split = splitDefinitions[idx];
128
+ splits[name] = split && JSON.parse(split);
128
129
  });
129
130
  return Promise.resolve(splits);
130
131
  });
@@ -135,10 +136,12 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
135
136
  * The returned promise is resolved with the list of split definitions,
136
137
  * or rejected if wrapper operation fails.
137
138
  */
138
- getAll(): Promise<string[]> {
139
- return this.wrapper.getKeysByPrefix(this.keys.buildSplitKeyPrefix()).then(
140
- (listOfKeys) => Promise.all(listOfKeys.map(this.wrapper.get) as Promise<string>[])
141
- );
139
+ getAll(): Promise<ISplit[]> {
140
+ return this.wrapper.getKeysByPrefix(this.keys.buildSplitKeyPrefix())
141
+ .then((listOfKeys) => this.wrapper.getMany(listOfKeys))
142
+ .then((splitDefinitions) => splitDefinitions.map((splitDefinition) => {
143
+ return JSON.parse(splitDefinition as string);
144
+ }));
142
145
  }
143
146
 
144
147
  /**
@@ -1,8 +1,13 @@
1
1
  import { ILogger } from '../../logger/types';
2
- import { Method } from '../../sync/submitters/types';
3
- import { KeyBuilderSS } from '../KeyBuilderSS';
2
+ import { Method, MultiConfigs, MultiMethodExceptions, MultiMethodLatencies } from '../../sync/submitters/types';
3
+ import { KeyBuilderSS, parseExceptionField, parseLatencyField, parseMetadata } from '../KeyBuilderSS';
4
4
  import { IPluggableStorageWrapper, ITelemetryCacheAsync } from '../types';
5
5
  import { findLatencyIndex } from '../findLatencyIndex';
6
+ import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter';
7
+ import { CONSUMER_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
8
+ import { isString, isNaNNumber } from '../../utils/lang';
9
+ import { _Map } from '../../utils/lang/maps';
10
+ import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory';
6
11
 
7
12
  export class TelemetryCachePluggable implements ITelemetryCacheAsync {
8
13
 
@@ -23,4 +28,144 @@ export class TelemetryCachePluggable implements ITelemetryCacheAsync {
23
28
  .catch(() => { /* Handle rejections for telemetry */ });
24
29
  }
25
30
 
31
+ recordConfig() {
32
+ const value = JSON.stringify(getTelemetryConfigStats(CONSUMER_MODE, STORAGE_PLUGGABLE));
33
+ return this.wrapper.set(this.keys.buildInitKey(), value).catch(() => { /* Handle rejections for telemetry */ });
34
+ }
35
+
36
+ /**
37
+ * Pop telemetry latencies.
38
+ * The returned promise rejects if wrapper operations fail.
39
+ */
40
+ popLatencies(): Promise<MultiMethodLatencies> {
41
+ return this.wrapper.getKeysByPrefix(this.keys.latencyPrefix).then(latencyKeys => {
42
+ return latencyKeys.length ?
43
+ this.wrapper.getMany(latencyKeys).then(latencies => {
44
+
45
+ const result: MultiMethodLatencies = new _Map();
46
+
47
+ for (let i = 0; i < latencyKeys.length; i++) {
48
+ const field = latencyKeys[i].split('::')[1];
49
+
50
+ const parsedField = parseLatencyField(field);
51
+ if (isString(parsedField)) {
52
+ this.log.error(`Ignoring invalid latency field: ${field}: ${parsedField}`);
53
+ continue;
54
+ }
55
+
56
+ // @ts-ignore
57
+ const count = parseInt(latencies[i]);
58
+ if (isNaNNumber(count)) {
59
+ this.log.error(`Ignoring latency with invalid count: ${latencies[i]}`);
60
+ continue;
61
+ }
62
+
63
+ const [metadata, method, bucket] = parsedField;
64
+
65
+ if (bucket >= MAX_LATENCY_BUCKET_COUNT) {
66
+ this.log.error(`Ignoring latency with invalid bucket: ${bucket}`);
67
+ continue;
68
+ }
69
+
70
+ if (!result.has(metadata)) result.set(metadata, {
71
+ t: newBuckets(),
72
+ ts: newBuckets(),
73
+ tc: newBuckets(),
74
+ tcs: newBuckets(),
75
+ tr: newBuckets(),
76
+ });
77
+
78
+ result.get(metadata)![method][bucket] = count;
79
+ }
80
+
81
+ return Promise.all(latencyKeys.map((latencyKey) => this.wrapper.del(latencyKey))).then(() => result);
82
+ }) :
83
+ // If latencyKeys is empty, return an empty map.
84
+ new _Map();
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Pop telemetry exceptions.
90
+ * The returned promise rejects if wrapper operations fail.
91
+ */
92
+ popExceptions(): Promise<MultiMethodExceptions> {
93
+ return this.wrapper.getKeysByPrefix(this.keys.exceptionPrefix).then(exceptionKeys => {
94
+ return exceptionKeys.length ?
95
+ this.wrapper.getMany(exceptionKeys).then(exceptions => {
96
+
97
+ const result: MultiMethodExceptions = new _Map();
98
+
99
+ for (let i = 0; i < exceptionKeys.length; i++) {
100
+ const field = exceptionKeys[i].split('::')[1];
101
+
102
+ const parsedField = parseExceptionField(field);
103
+ if (isString(parsedField)) {
104
+ this.log.error(`Ignoring invalid exception field: ${field}: ${parsedField}`);
105
+ continue;
106
+ }
107
+
108
+ // @ts-ignore
109
+ const count = parseInt(exceptions[i]);
110
+ if (isNaNNumber(count)) {
111
+ this.log.error(`Ignoring exception with invalid count: ${exceptions[i]}`);
112
+ continue;
113
+ }
114
+
115
+ const [metadata, method] = parsedField;
116
+
117
+ if (!result.has(metadata)) result.set(metadata, {
118
+ t: 0,
119
+ ts: 0,
120
+ tc: 0,
121
+ tcs: 0,
122
+ tr: 0,
123
+ });
124
+
125
+ result.get(metadata)![method] = count;
126
+ }
127
+
128
+ return Promise.all(exceptionKeys.map((exceptionKey) => this.wrapper.del(exceptionKey))).then(() => result);
129
+ }) :
130
+ // If exceptionKeys is empty, return an empty map.
131
+ new _Map();
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Pop telemetry configs.
137
+ * The returned promise rejects if wrapper operations fail.
138
+ */
139
+ popConfigs(): Promise<MultiConfigs> {
140
+ return this.wrapper.getKeysByPrefix(this.keys.initPrefix).then(configKeys => {
141
+ return configKeys.length ?
142
+ this.wrapper.getMany(configKeys).then(configs => {
143
+
144
+ const result: MultiConfigs = new _Map();
145
+
146
+ for (let i = 0; i < configKeys.length; i++) {
147
+ const field = configKeys[i].split('::')[1];
148
+
149
+ const parsedField = parseMetadata(field);
150
+ if (isString(parsedField)) {
151
+ this.log.error(`Ignoring invalid config field: ${field}: ${parsedField}`);
152
+ continue;
153
+ }
154
+
155
+ const [metadata] = parsedField;
156
+
157
+ try { // @ts-ignore
158
+ const config = JSON.parse(configs[i]);
159
+ result.set(metadata, config);
160
+ } catch (e) {
161
+ this.log.error(`Ignoring invalid config: ${configs[i]}`);
162
+ }
163
+ }
164
+
165
+ return Promise.all(configKeys.map((configKey) => this.wrapper.del(configKey))).then(() => result);
166
+ }) :
167
+ // If configKeys is empty, return an empty map.
168
+ new _Map();
169
+ });
170
+ }
26
171
  }
@@ -0,0 +1,56 @@
1
+ import { IPluggableStorageWrapper, IUniqueKeysCacheBase } from '../types';
2
+ import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
3
+ import { setToArray } from '../../utils/lang/sets';
4
+ import { DEFAULT_CACHE_SIZE, REFRESH_RATE } from '../inRedis/constants';
5
+ import { LOG_PREFIX } from './constants';
6
+ import { ILogger } from '../../logger/types';
7
+
8
+ export class UniqueKeysCachePluggable extends UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
9
+
10
+ private readonly log: ILogger;
11
+ private readonly key: string;
12
+ private readonly wrapper: IPluggableStorageWrapper;
13
+ private readonly refreshRate: number;
14
+ private intervalId: any;
15
+
16
+ constructor(log: ILogger, key: string, wrapper: IPluggableStorageWrapper, uniqueKeysQueueSize = DEFAULT_CACHE_SIZE, refreshRate = REFRESH_RATE) {
17
+ super(uniqueKeysQueueSize);
18
+ this.log = log;
19
+ this.key = key;
20
+ this.wrapper = wrapper;
21
+ this.refreshRate = refreshRate;
22
+ this.onFullQueue = () => { this.storeUniqueKeys(); };
23
+ }
24
+
25
+ storeUniqueKeys() {
26
+ const featureNames = Object.keys(this.uniqueKeysTracker);
27
+ if (!featureNames.length) return Promise.resolve(false);
28
+
29
+ const pipeline = featureNames.reduce<Promise<any>>((pipeline, featureName) => {
30
+ const featureKeys = setToArray(this.uniqueKeysTracker[featureName]);
31
+ const uniqueKeysPayload = {
32
+ f: featureName,
33
+ ks: featureKeys
34
+ };
35
+
36
+ return pipeline.then(() => this.wrapper.pushItems(this.key, [JSON.stringify(uniqueKeysPayload)]));
37
+ }, Promise.resolve());
38
+
39
+ this.clear();
40
+ return pipeline.catch(err => {
41
+ this.log.error(`${LOG_PREFIX}Error in uniqueKeys pipeline: ${err}.`);
42
+ return false;
43
+ });
44
+ }
45
+
46
+
47
+ start() {
48
+ this.intervalId = setInterval(this.storeUniqueKeys.bind(this), this.refreshRate);
49
+ }
50
+
51
+ stop() {
52
+ clearInterval(this.intervalId);
53
+ return this.storeUniqueKeys();
54
+ }
55
+
56
+ }
@@ -1,4 +1,4 @@
1
- import { IPluggableStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams } from '../types';
1
+ import { IPluggableStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams, ITelemetryCacheAsync } from '../types';
2
2
 
3
3
  import { KeyBuilderSS } from '../KeyBuilderSS';
4
4
  import { SplitsCachePluggable } from './SplitsCachePluggable';
@@ -8,10 +8,16 @@ import { EventsCachePluggable } from './EventsCachePluggable';
8
8
  import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter';
9
9
  import { isObject } from '../../utils/lang';
10
10
  import { validatePrefix } from '../KeyBuilder';
11
- import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
11
+ import { CONSUMER_PARTIAL_MODE, DEBUG, NONE, STORAGE_PLUGGABLE } from '../../utils/constants';
12
12
  import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory';
13
13
  import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
14
14
  import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
15
+ import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
16
+ import { TelemetryCachePluggable } from './TelemetryCachePluggable';
17
+ import { ImpressionCountsCachePluggable } from './ImpressionCountsCachePluggable';
18
+ import { UniqueKeysCachePluggable } from './UniqueKeysCachePluggable';
19
+ import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
20
+ import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
15
21
 
16
22
  const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
17
23
  const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
@@ -35,16 +41,6 @@ function validatePluggableStorageOptions(options: any) {
35
41
  if (missingMethods.length) throw new Error(`${NO_VALID_WRAPPER_INTERFACE} The following methods are missing or invalid: ${missingMethods}`);
36
42
  }
37
43
 
38
- // subscription to wrapper connect event in order to emit SDK_READY event
39
- function wrapperConnect(wrapper: IPluggableStorageWrapper, onReadyCb: (error?: any) => void) {
40
- wrapper.connect().then(() => {
41
- onReadyCb();
42
- // At the moment, we don't synchronize config with pluggable storage
43
- }).catch((e) => {
44
- onReadyCb(e || new Error('Error connecting wrapper'));
45
- });
46
- }
47
-
48
44
  // Async return type in `client.track` method on consumer partial mode
49
45
  // No need to promisify impressions cache
50
46
  function promisifyEventsTrack(events: any) {
@@ -64,31 +60,60 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
64
60
 
65
61
  const prefix = validatePrefix(options.prefix);
66
62
 
67
- function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize, impressionsQueueSize, optimize }: IStorageFactoryParams): IStorageAsync {
63
+ function PluggableStorageFactory(params: IStorageFactoryParams): IStorageAsync {
64
+ const { log, metadata, onReadyCb, mode, eventsQueueSize, impressionsQueueSize, impressionsMode, matchingKey } = params;
68
65
  const keys = new KeyBuilderSS(prefix, metadata);
69
66
  const wrapper = wrapperAdapter(log, options.wrapper);
70
67
  const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
68
+ const telemetry = shouldRecordTelemetry(params) ?
69
+ isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper) :
70
+ undefined;
71
+ const impressionCountsCache = impressionsMode !== DEBUG ?
72
+ isPartialConsumer ? new ImpressionCountsCacheInMemory() : new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper) :
73
+ undefined;
74
+ const uniqueKeysCache = impressionsMode === NONE ?
75
+ isPartialConsumer ?
76
+ matchingKey === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
77
+ new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
78
+ undefined;
71
79
 
72
80
  // Connects to wrapper and emits SDK_READY event on main client
73
- wrapperConnect(wrapper, onReadyCb);
81
+ const connectPromise = wrapper.connect().then(() => {
82
+ onReadyCb();
83
+
84
+ // Start periodic flush of async storages
85
+ if (impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
86
+ if (uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
87
+
88
+ // If mode is not defined, it means that the synchronizer is running and so we don't have to record telemetry
89
+ if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig && mode) (telemetry as ITelemetryCacheAsync).recordConfig();
90
+ }).catch((e) => {
91
+ e = e || new Error('Error connecting wrapper');
92
+ onReadyCb(e);
93
+ return e;
94
+ });
74
95
 
75
96
  return {
76
97
  splits: new SplitsCachePluggable(log, keys, wrapper),
77
98
  segments: new SegmentsCachePluggable(log, keys, wrapper),
78
99
  impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
79
- impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined,
100
+ impressionCounts: impressionCountsCache,
80
101
  events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
81
- // @TODO Not using TelemetryCachePluggable yet because it's not supported by the Split Synchronizer, and needs to drop or queue operations while the wrapper is not ready
82
- // telemetry: isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper),
102
+ telemetry,
103
+ uniqueKeys: uniqueKeysCache,
83
104
 
84
105
  // Disconnect the underlying storage
85
106
  destroy() {
86
- return wrapper.disconnect();
107
+ return Promise.all([
108
+ impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
109
+ uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
110
+ ]).then(() => wrapper.disconnect());
87
111
  },
88
112
 
89
113
  // emits SDK_READY event on shared clients and returns a reference to the storage
90
114
  shared(_, onReadyCb) {
91
- wrapperConnect(wrapper, onReadyCb);
115
+ connectPromise.then(onReadyCb);
116
+
92
117
  return {
93
118
  ...this,
94
119
  // no-op destroy, to disconnect the wrapper only when the main client is destroyed
@@ -1,8 +1,7 @@
1
- import { MaybeThenable, IMetadata, ISplitFiltersValidation } from '../dtos/types';
1
+ import { MaybeThenable, IMetadata, ISplitFiltersValidation, ISplit } from '../dtos/types';
2
2
  import { ILogger } from '../logger/types';
3
- import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent } from '../sync/submitters/types';
3
+ import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs } from '../sync/submitters/types';
4
4
  import { SplitIO, ImpressionDTO, SDKMode } from '../types';
5
- import { ISet } from '../utils/lang/sets';
6
5
 
7
6
  /**
8
7
  * Interface of a pluggable storage wrapper.
@@ -71,23 +70,25 @@ export interface IPluggableStorageWrapper {
71
70
  /** Integer operations */
72
71
 
73
72
  /**
74
- * Increments in 1 the given `key` value or set it to 1 if the value doesn't exist.
73
+ * Increments the number stored at `key` by `increment` (or 1 if `increment` is not provided), or set it to `increment` (or 1) if the value doesn't exist.
75
74
  *
76
75
  * @function incr
77
76
  * @param {string} key Key to increment
77
+ * @param {number} increment Value to increment by
78
78
  * @returns {Promise<number>} A promise that resolves with the value of key after the increment. The promise rejects if the operation fails,
79
79
  * for example, if there is a connection error or the key contains a string that can not be represented as integer.
80
80
  */
81
- incr: (key: string) => Promise<number>
81
+ incr: (key: string, increment?: number) => Promise<number>
82
82
  /**
83
- * Decrements in 1 the given `key` value or set it to -1 if the value doesn't exist.
83
+ * Decrements the number stored at `key` by `decrement` (or 1 if `decrement` is not provided), or set it to minus `decrement` (or minus 1) if the value doesn't exist.
84
84
  *
85
85
  * @function decr
86
86
  * @param {string} key Key to decrement
87
+ * @param {number} decrement Value to decrement by
87
88
  * @returns {Promise<number>} A promise that resolves with the value of key after the decrement. The promise rejects if the operation fails,
88
89
  * for example, if there is a connection error or the key contains a string that can not be represented as integer.
89
90
  */
90
- decr: (key: string) => Promise<number>
91
+ decr: (key: string, decrement?: number) => Promise<number>
91
92
 
92
93
  /** Queue operations */
93
94
 
@@ -192,14 +193,14 @@ export interface IPluggableStorageWrapper {
192
193
  /** Splits cache */
193
194
 
194
195
  export interface ISplitsCacheBase {
195
- addSplits(entries: [string, string][]): MaybeThenable<boolean[] | void>,
196
+ addSplits(entries: [string, ISplit][]): MaybeThenable<boolean[] | void>,
196
197
  removeSplits(names: string[]): MaybeThenable<boolean[] | void>,
197
- getSplit(name: string): MaybeThenable<string | null>,
198
- getSplits(names: string[]): MaybeThenable<Record<string, string | null>>, // `fetchMany` in spec
198
+ getSplit(name: string): MaybeThenable<ISplit | null>,
199
+ getSplits(names: string[]): MaybeThenable<Record<string, ISplit | null>>, // `fetchMany` in spec
199
200
  setChangeNumber(changeNumber: number): MaybeThenable<boolean | void>,
200
201
  // should never reject or throw an exception. Instead return -1 by default, assuming no splits are present in the storage.
201
202
  getChangeNumber(): MaybeThenable<number>,
202
- getAll(): MaybeThenable<string[]>,
203
+ getAll(): MaybeThenable<ISplit[]>,
203
204
  getSplitNames(): MaybeThenable<string[]>,
204
205
  // should never reject or throw an exception. Instead return true by default, asssuming the TT might exist.
205
206
  trafficTypeExists(trafficType: string): MaybeThenable<boolean>,
@@ -212,13 +213,13 @@ export interface ISplitsCacheBase {
212
213
  }
213
214
 
214
215
  export interface ISplitsCacheSync extends ISplitsCacheBase {
215
- addSplits(entries: [string, string][]): boolean[],
216
+ addSplits(entries: [string, ISplit][]): boolean[],
216
217
  removeSplits(names: string[]): boolean[],
217
- getSplit(name: string): string | null,
218
- getSplits(names: string[]): Record<string, string | null>,
218
+ getSplit(name: string): ISplit | null,
219
+ getSplits(names: string[]): Record<string, ISplit | null>,
219
220
  setChangeNumber(changeNumber: number): boolean,
220
221
  getChangeNumber(): number,
221
- getAll(): string[],
222
+ getAll(): ISplit[],
222
223
  getSplitNames(): string[],
223
224
  trafficTypeExists(trafficType: string): boolean,
224
225
  usesSegments(): boolean,
@@ -228,13 +229,13 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
228
229
  }
229
230
 
230
231
  export interface ISplitsCacheAsync extends ISplitsCacheBase {
231
- addSplits(entries: [string, string][]): Promise<boolean[] | void>,
232
+ addSplits(entries: [string, ISplit][]): Promise<boolean[] | void>,
232
233
  removeSplits(names: string[]): Promise<boolean[] | void>,
233
- getSplit(name: string): Promise<string | null>,
234
- getSplits(names: string[]): Promise<Record<string, string | null>>,
234
+ getSplit(name: string): Promise<ISplit | null>,
235
+ getSplits(names: string[]): Promise<Record<string, ISplit | null>>,
235
236
  setChangeNumber(changeNumber: number): Promise<boolean | void>,
236
237
  getChangeNumber(): Promise<number>,
237
- getAll(): Promise<string[]>,
238
+ getAll(): Promise<ISplit[]>,
238
239
  getSplitNames(): Promise<string[]>,
239
240
  trafficTypeExists(trafficType: string): Promise<boolean>,
240
241
  usesSegments(): Promise<boolean>,
@@ -348,7 +349,7 @@ export interface IEventsCacheAsync extends IEventsCacheBase, IRecorderCacheProdu
348
349
  * Only in memory. Named `ImpressionsCounter` in spec.
349
350
  */
350
351
  export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<Record<string, number>> {
351
- // Used by impressions tracker
352
+ // Used by impressions tracker
352
353
  track(featureName: string, timeFrame: number, amount: number): void
353
354
 
354
355
  // Used by impressions count submitter in standalone and producer mode
@@ -356,13 +357,16 @@ export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<R
356
357
  pop(toMerge?: Record<string, number> ): Record<string, number> // pop cache data
357
358
  }
358
359
 
359
- export interface IUniqueKeysCacheBase extends IRecorderCacheProducerSync<{ [featureName: string]: ISet<string> }>{
360
+ export interface IUniqueKeysCacheBase {
360
361
  // Used by unique Keys tracker
361
- track(featureName: string, timeFrame: number, amount: number): void
362
+ track(key: string, value: string): void
362
363
 
363
364
  // Used by unique keys submitter in standalone and producer mode
364
365
  isEmpty(): boolean // check if cache is empty. Return true if the cache was just created or cleared.
365
- pop(toMerge?: { [featureName: string]: ISet<string> } ): { [featureName: string]: ISet<string> } // pop cache data
366
+ pop(): UniqueKeysPayloadSs | UniqueKeysPayloadCs // pop cache data
367
+ /* Registers callback for full queue */
368
+ setOnFullQueueCb(cb: () => void): void,
369
+ clear(): void
366
370
  }
367
371
 
368
372
  /**
@@ -432,18 +436,19 @@ export interface ITelemetryCacheSync extends ITelemetryStorageConsumerSync, ITel
432
436
  */
433
437
 
434
438
  export interface ITelemetryEvaluationConsumerAsync {
435
- popExceptions(): Promise<MethodExceptions>;
436
- popLatencies(): Promise<MethodLatencies>;
439
+ popLatencies(): Promise<MultiMethodLatencies>;
440
+ popExceptions(): Promise<MultiMethodExceptions>;
441
+ popConfigs(): Promise<MultiConfigs>;
437
442
  }
438
443
 
439
444
  export interface ITelemetryEvaluationProducerAsync {
440
445
  recordLatency(method: Method, latencyMs: number): Promise<any>;
441
446
  recordException(method: Method): Promise<any>;
447
+ recordConfig(): Promise<any>;
442
448
  }
443
449
 
444
450
  // ATM it only implements the producer API, used by the SDK in consumer mode.
445
- // @TODO implement consumer API for JS Synchronizer.
446
- export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync { }
451
+ export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync, ITelemetryEvaluationConsumerAsync { }
447
452
 
448
453
  /**
449
454
  * Storages
@@ -453,6 +458,7 @@ export interface IStorageBase<
453
458
  TSplitsCache extends ISplitsCacheBase,
454
459
  TSegmentsCache extends ISegmentsCacheBase,
455
460
  TImpressionsCache extends IImpressionsCacheBase,
461
+ TImpressionsCountCache extends IImpressionCountsCacheSync,
456
462
  TEventsCache extends IEventsCacheBase,
457
463
  TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync,
458
464
  TUniqueKeysCache extends IUniqueKeysCacheBase
@@ -460,7 +466,7 @@ export interface IStorageBase<
460
466
  splits: TSplitsCache,
461
467
  segments: TSegmentsCache,
462
468
  impressions: TImpressionsCache,
463
- impressionCounts?: IImpressionCountsCacheSync,
469
+ impressionCounts?: TImpressionsCountCache,
464
470
  events: TEventsCache,
465
471
  telemetry?: TTelemetryCache,
466
472
  uniqueKeys?: TUniqueKeysCache,
@@ -472,6 +478,7 @@ export interface IStorageSync extends IStorageBase<
472
478
  ISplitsCacheSync,
473
479
  ISegmentsCacheSync,
474
480
  IImpressionsCacheSync,
481
+ IImpressionCountsCacheSync,
475
482
  IEventsCacheSync,
476
483
  ITelemetryCacheSync,
477
484
  IUniqueKeysCacheBase
@@ -481,8 +488,9 @@ export interface IStorageAsync extends IStorageBase<
481
488
  ISplitsCacheAsync,
482
489
  ISegmentsCacheAsync,
483
490
  IImpressionsCacheAsync | IImpressionsCacheSync,
491
+ IImpressionCountsCacheSync,
484
492
  IEventsCacheAsync | IEventsCacheSync,
485
- ITelemetryCacheAsync,
493
+ ITelemetryCacheAsync | ITelemetryCacheSync,
486
494
  IUniqueKeysCacheBase
487
495
  > { }
488
496
 
@@ -496,7 +504,7 @@ export interface IStorageFactoryParams {
496
504
  eventsQueueSize?: number,
497
505
  optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */,
498
506
  mode: SDKMode,
499
-
507
+ impressionsMode?: string,
500
508
  // ATM, only used by InLocalStorage
501
509
  matchingKey?: string, /* undefined on server-side SDKs */
502
510
  splitFiltersValidation?: ISplitFiltersValidation,