@splitsoftware/splitio-commons 1.6.2-rc.7 → 1.6.2-rc.9

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 (164) hide show
  1. package/CHANGES.txt +2 -1
  2. package/cjs/consent/sdkUserConsent.js +2 -2
  3. package/cjs/evaluator/index.js +5 -5
  4. package/cjs/listeners/browser.js +1 -2
  5. package/cjs/logger/constants.js +1 -2
  6. package/cjs/sdkClient/client.js +19 -7
  7. package/cjs/sdkClient/sdkClient.js +1 -3
  8. package/cjs/sdkFactory/index.js +5 -26
  9. package/cjs/services/splitApi.js +4 -24
  10. package/cjs/storages/KeyBuilderSS.js +48 -15
  11. package/cjs/storages/inLocalStorage/index.js +1 -5
  12. package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -12
  13. package/cjs/storages/inMemory/InMemoryStorage.js +2 -6
  14. package/cjs/storages/inMemory/InMemoryStorageCS.js +2 -6
  15. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +9 -6
  16. package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
  17. package/cjs/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  18. package/cjs/storages/inRedis/constants.js +1 -4
  19. package/cjs/storages/inRedis/index.js +1 -15
  20. package/cjs/storages/pluggable/TelemetryCachePluggable.js +126 -0
  21. package/cjs/storages/pluggable/index.js +19 -15
  22. package/cjs/sync/submitters/submitterManager.js +0 -3
  23. package/cjs/sync/submitters/telemetrySubmitter.js +7 -5
  24. package/cjs/trackers/impressionsTracker.js +41 -22
  25. package/cjs/utils/constants/index.js +2 -4
  26. package/cjs/utils/lang/maps.js +15 -7
  27. package/cjs/utils/settingsValidation/impressionsMode.js +2 -2
  28. package/cjs/utils/settingsValidation/index.js +0 -4
  29. package/esm/consent/sdkUserConsent.js +2 -2
  30. package/esm/evaluator/index.js +5 -5
  31. package/esm/listeners/browser.js +2 -3
  32. package/esm/logger/constants.js +0 -1
  33. package/esm/sdkClient/client.js +19 -7
  34. package/esm/sdkClient/sdkClient.js +1 -3
  35. package/esm/sdkFactory/index.js +5 -26
  36. package/esm/services/splitApi.js +4 -24
  37. package/esm/storages/KeyBuilderSS.js +44 -14
  38. package/esm/storages/inLocalStorage/index.js +2 -6
  39. package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -12
  40. package/esm/storages/inMemory/InMemoryStorage.js +4 -8
  41. package/esm/storages/inMemory/InMemoryStorageCS.js +3 -7
  42. package/esm/storages/inMemory/TelemetryCacheInMemory.js +8 -6
  43. package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
  44. package/esm/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  45. package/esm/storages/inRedis/constants.js +0 -3
  46. package/esm/storages/inRedis/index.js +2 -16
  47. package/esm/storages/pluggable/TelemetryCachePluggable.js +126 -0
  48. package/esm/storages/pluggable/index.js +19 -15
  49. package/esm/sync/submitters/submitterManager.js +0 -3
  50. package/esm/sync/submitters/telemetrySubmitter.js +8 -6
  51. package/esm/trackers/impressionsTracker.js +41 -22
  52. package/esm/utils/constants/index.js +0 -2
  53. package/esm/utils/lang/maps.js +15 -7
  54. package/esm/utils/settingsValidation/impressionsMode.js +3 -3
  55. package/esm/utils/settingsValidation/index.js +0 -4
  56. package/package.json +2 -1
  57. package/src/consent/sdkUserConsent.ts +2 -2
  58. package/src/evaluator/index.ts +6 -6
  59. package/src/listeners/browser.ts +2 -3
  60. package/src/logger/.DS_Store +0 -0
  61. package/src/logger/constants.ts +0 -1
  62. package/src/sdkClient/client.ts +21 -8
  63. package/src/sdkClient/sdkClient.ts +1 -3
  64. package/src/sdkFactory/index.ts +5 -29
  65. package/src/sdkFactory/types.ts +4 -7
  66. package/src/services/splitApi.ts +4 -26
  67. package/src/services/types.ts +2 -8
  68. package/src/storages/KeyBuilderSS.ts +53 -17
  69. package/src/storages/inLocalStorage/index.ts +2 -5
  70. package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +1 -16
  71. package/src/storages/inMemory/InMemoryStorage.ts +4 -7
  72. package/src/storages/inMemory/InMemoryStorageCS.ts +3 -7
  73. package/src/storages/inMemory/TelemetryCacheInMemory.ts +9 -7
  74. package/src/storages/inRedis/EventsCacheInRedis.ts +1 -1
  75. package/src/storages/inRedis/TelemetryCacheInRedis.ts +122 -2
  76. package/src/storages/inRedis/constants.ts +0 -3
  77. package/src/storages/inRedis/index.ts +3 -12
  78. package/src/storages/pluggable/TelemetryCachePluggable.ts +147 -2
  79. package/src/storages/pluggable/index.ts +20 -16
  80. package/src/storages/types.ts +13 -34
  81. package/src/sync/submitters/submitterManager.ts +0 -2
  82. package/src/sync/submitters/telemetrySubmitter.ts +14 -9
  83. package/src/sync/submitters/types.ts +40 -26
  84. package/src/trackers/impressionsTracker.ts +48 -27
  85. package/src/trackers/types.ts +0 -28
  86. package/src/types.ts +1 -5
  87. package/src/utils/constants/index.ts +0 -2
  88. package/src/utils/lang/maps.ts +20 -8
  89. package/src/utils/settingsValidation/impressionsMode.ts +3 -3
  90. package/src/utils/settingsValidation/index.ts +0 -5
  91. package/types/logger/constants.d.ts +0 -1
  92. package/types/sdkFactory/types.d.ts +2 -4
  93. package/types/services/types.d.ts +2 -6
  94. package/types/storages/KeyBuilderSS.d.ts +7 -4
  95. package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +1 -5
  96. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +6 -3
  97. package/types/storages/inRedis/EventsCacheInRedis.d.ts +1 -1
  98. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +16 -1
  99. package/types/storages/inRedis/constants.d.ts +0 -3
  100. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +17 -1
  101. package/types/storages/types.d.ts +10 -21
  102. package/types/sync/submitters/telemetrySubmitter.d.ts +1 -1
  103. package/types/sync/submitters/types.d.ts +13 -24
  104. package/types/trackers/impressionsTracker.d.ts +6 -4
  105. package/types/trackers/types.d.ts +0 -23
  106. package/types/types.d.ts +1 -5
  107. package/types/utils/constants/index.d.ts +0 -2
  108. package/types/utils/lang/maps.d.ts +6 -2
  109. package/types/utils/settingsValidation/index.d.ts +0 -1
  110. package/cjs/storages/inMemory/uniqueKeysCacheInMemory.js +0 -73
  111. package/cjs/storages/inMemory/uniqueKeysCacheInMemoryCS.js +0 -78
  112. package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +0 -49
  113. package/cjs/storages/inRedis/uniqueKeysCacheInRedis.js +0 -56
  114. package/cjs/sync/submitters/uniqueKeysSubmitter.js +0 -26
  115. package/cjs/trackers/strategy/strategyDebug.js +0 -25
  116. package/cjs/trackers/strategy/strategyNone.js +0 -29
  117. package/cjs/trackers/strategy/strategyOptimized.js +0 -35
  118. package/cjs/trackers/uniqueKeysTracker.js +0 -38
  119. package/esm/storages/inMemory/uniqueKeysCacheInMemory.js +0 -70
  120. package/esm/storages/inMemory/uniqueKeysCacheInMemoryCS.js +0 -75
  121. package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +0 -46
  122. package/esm/storages/inRedis/uniqueKeysCacheInRedis.js +0 -53
  123. package/esm/sync/submitters/uniqueKeysSubmitter.js +0 -22
  124. package/esm/trackers/strategy/strategyDebug.js +0 -21
  125. package/esm/trackers/strategy/strategyNone.js +0 -25
  126. package/esm/trackers/strategy/strategyOptimized.js +0 -31
  127. package/esm/trackers/uniqueKeysTracker.js +0 -34
  128. package/src/storages/inMemory/uniqueKeysCacheInMemory.ts +0 -82
  129. package/src/storages/inMemory/uniqueKeysCacheInMemoryCS.ts +0 -88
  130. package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +0 -51
  131. package/src/storages/inRedis/uniqueKeysCacheInRedis.ts +0 -63
  132. package/src/sync/submitters/uniqueKeysSubmitter.ts +0 -35
  133. package/src/trackers/strategy/strategyDebug.ts +0 -28
  134. package/src/trackers/strategy/strategyNone.ts +0 -34
  135. package/src/trackers/strategy/strategyOptimized.ts +0 -42
  136. package/src/trackers/uniqueKeysTracker.ts +0 -48
  137. package/types/sdkClient/types.d.ts +0 -18
  138. package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
  139. package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
  140. package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +0 -35
  141. package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +0 -37
  142. package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
  143. package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +0 -14
  144. package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
  145. package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +0 -15
  146. package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
  147. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
  148. package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
  149. package/types/sync/submitters/impressionCountsSubmitterInRedis.d.ts +0 -5
  150. package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
  151. package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
  152. package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
  153. package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
  154. package/types/sync/submitters/uniqueKeysSubmitter.d.ts +0 -5
  155. package/types/sync/submitters/uniqueKeysSubmitterInRedis.d.ts +0 -5
  156. package/types/sync/syncTaskComposite.d.ts +0 -5
  157. package/types/trackers/filter/bloomFilter.d.ts +0 -10
  158. package/types/trackers/filter/dictionaryFilter.d.ts +0 -8
  159. package/types/trackers/filter/types.d.ts +0 -5
  160. package/types/trackers/strategy/strategyDebug.d.ts +0 -9
  161. package/types/trackers/strategy/strategyNone.d.ts +0 -10
  162. package/types/trackers/strategy/strategyOptimized.d.ts +0 -11
  163. package/types/trackers/uniqueKeysTracker.d.ts +0 -13
  164. package/types/utils/timeTracker/index.d.ts +0 -70
@@ -6,10 +6,8 @@ import { SplitsCacheInRedis } from './SplitsCacheInRedis';
6
6
  import { SegmentsCacheInRedis } from './SegmentsCacheInRedis';
7
7
  import { ImpressionsCacheInRedis } from './ImpressionsCacheInRedis';
8
8
  import { EventsCacheInRedis } from './EventsCacheInRedis';
9
- import { DEBUG, NONE, STORAGE_REDIS } from '../../utils/constants';
9
+ import { STORAGE_REDIS } from '../../utils/constants';
10
10
  import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
11
- import { UniqueKeysCacheInRedis } from './uniqueKeysCacheInRedis';
12
- import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
13
11
 
14
12
  export interface InRedisStorageOptions {
15
13
  prefix?: string
@@ -24,18 +22,15 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
24
22
 
25
23
  const prefix = validatePrefix(options.prefix);
26
24
 
27
- function InRedisStorageFactory({ log, metadata, onReadyCb, impressionsMode, impressionCountsQueueSize, impressionCountsRefreshRate, uniqueKeysCacheSize, uniqueKeysRefreshRate }: IStorageFactoryParams): IStorageAsync {
25
+ function InRedisStorageFactory({ log, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync {
26
+
28
27
  const keys = new KeyBuilderSS(prefix, metadata);
29
28
  const redisClient = new RedisAdapter(log, options.options || {});
30
29
  const telemetry = new TelemetryCacheInRedis(log, keys, redisClient);
31
- const impressionCountsCache = impressionsMode !== DEBUG ? new ImpressionCountsCacheInRedis(log, keys.buildImpressionsCountKey(), redisClient, impressionCountsQueueSize, impressionCountsRefreshRate) : undefined;
32
- const uniqueKeysCache = impressionsMode === NONE ? new UniqueKeysCacheInRedis(log, keys.buildUniqueKeysKey(), redisClient, uniqueKeysCacheSize, uniqueKeysRefreshRate) : undefined;
33
30
 
34
31
  // subscription to Redis connect event in order to emit SDK_READY event on consumer mode
35
32
  redisClient.on('connect', () => {
36
33
  onReadyCb();
37
- if (impressionCountsCache) impressionCountsCache.start();
38
- if (uniqueKeysCache) uniqueKeysCache.start();
39
34
 
40
35
  // Synchronize config
41
36
  telemetry.recordConfig();
@@ -45,17 +40,13 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
45
40
  splits: new SplitsCacheInRedis(log, keys, redisClient),
46
41
  segments: new SegmentsCacheInRedis(log, keys, redisClient),
47
42
  impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
48
- impressionCounts: impressionCountsCache,
49
43
  events: new EventsCacheInRedis(log, keys.buildEventsKey(), redisClient, metadata),
50
44
  telemetry,
51
- uniqueKeys: uniqueKeysCache,
52
45
 
53
46
  // When using REDIS we should:
54
47
  // 1- Disconnect from the storage
55
48
  destroy() {
56
49
  redisClient.disconnect();
57
- if (impressionCountsCache) impressionCountsCache.stop();
58
- if (uniqueKeysCache) uniqueKeysCache.stop();
59
50
  // @TODO check that caches works as expected when redisClient is disconnected
60
51
  }
61
52
  };
@@ -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
  }
@@ -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';
@@ -12,6 +12,8 @@ import { CONSUMER_PARTIAL_MODE, 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';
15
17
 
16
18
  const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
17
19
  const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
@@ -35,16 +37,6 @@ function validatePluggableStorageOptions(options: any) {
35
37
  if (missingMethods.length) throw new Error(`${NO_VALID_WRAPPER_INTERFACE} The following methods are missing or invalid: ${missingMethods}`);
36
38
  }
37
39
 
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
40
  // Async return type in `client.track` method on consumer partial mode
49
41
  // No need to promisify impressions cache
50
42
  function promisifyEventsTrack(events: any) {
@@ -64,13 +56,25 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
64
56
 
65
57
  const prefix = validatePrefix(options.prefix);
66
58
 
67
- function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize, impressionsQueueSize, optimize }: IStorageFactoryParams): IStorageAsync {
59
+ function PluggableStorageFactory(params: IStorageFactoryParams): IStorageAsync {
60
+ const { log, metadata, onReadyCb, mode, eventsQueueSize, impressionsQueueSize, optimize } = params;
68
61
  const keys = new KeyBuilderSS(prefix, metadata);
69
62
  const wrapper = wrapperAdapter(log, options.wrapper);
70
63
  const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
64
+ const telemetry = shouldRecordTelemetry(params) ?
65
+ isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper) :
66
+ undefined;
71
67
 
72
68
  // Connects to wrapper and emits SDK_READY event on main client
73
- wrapperConnect(wrapper, onReadyCb);
69
+ const connectPromise = wrapper.connect().then(() => {
70
+ onReadyCb();
71
+ // If mode is not defined, it means that the synchronizer is running and so we don't have to record telemetry
72
+ if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig && mode) (telemetry as ITelemetryCacheAsync).recordConfig();
73
+ }).catch((e) => {
74
+ e = e || new Error('Error connecting wrapper');
75
+ onReadyCb(e);
76
+ return e;
77
+ });
74
78
 
75
79
  return {
76
80
  splits: new SplitsCachePluggable(log, keys, wrapper),
@@ -78,8 +82,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
78
82
  impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
79
83
  impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined,
80
84
  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),
85
+ telemetry,
83
86
 
84
87
  // Disconnect the underlying storage
85
88
  destroy() {
@@ -88,7 +91,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
88
91
 
89
92
  // emits SDK_READY event on shared clients and returns a reference to the storage
90
93
  shared(_, onReadyCb) {
91
- wrapperConnect(wrapper, onReadyCb);
94
+ connectPromise.then(onReadyCb);
95
+
92
96
  return {
93
97
  ...this,
94
98
  // no-op destroy, to disconnect the wrapper only when the main client is destroyed
@@ -1,6 +1,6 @@
1
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, UniqueKeysPayloadCs, UniqueKeysPayloadSs } from '../sync/submitters/types';
3
+ import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent } from '../sync/submitters/types';
4
4
  import { SplitIO, ImpressionDTO, SDKMode } from '../types';
5
5
 
6
6
  /**
@@ -347,7 +347,7 @@ export interface IEventsCacheAsync extends IEventsCacheBase, IRecorderCacheProdu
347
347
  * Only in memory. Named `ImpressionsCounter` in spec.
348
348
  */
349
349
  export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<Record<string, number>> {
350
- // Used by impressions tracker
350
+ // Used by impressions tracker
351
351
  track(featureName: string, timeFrame: number, amount: number): void
352
352
 
353
353
  // Used by impressions count submitter in standalone and producer mode
@@ -355,17 +355,6 @@ export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<R
355
355
  pop(toMerge?: Record<string, number> ): Record<string, number> // pop cache data
356
356
  }
357
357
 
358
- export interface IUniqueKeysCacheBase {
359
- // Used by unique Keys tracker
360
- track(key: string, value: string): void
361
-
362
- // Used by unique keys submitter in standalone and producer mode
363
- isEmpty(): boolean // check if cache is empty. Return true if the cache was just created or cleared.
364
- pop(): UniqueKeysPayloadSs | UniqueKeysPayloadCs // pop cache data
365
- /* Registers callback for full queue */
366
- setOnFullQueueCb(cb: () => void): void,
367
- clear(): void
368
- }
369
358
 
370
359
  /**
371
360
  * Telemetry storage interface for standalone and partial consumer modes.
@@ -434,18 +423,19 @@ export interface ITelemetryCacheSync extends ITelemetryStorageConsumerSync, ITel
434
423
  */
435
424
 
436
425
  export interface ITelemetryEvaluationConsumerAsync {
437
- popExceptions(): Promise<MethodExceptions>;
438
- popLatencies(): Promise<MethodLatencies>;
426
+ popLatencies(): Promise<MultiMethodLatencies>;
427
+ popExceptions(): Promise<MultiMethodExceptions>;
428
+ popConfigs(): Promise<MultiConfigs>;
439
429
  }
440
430
 
441
431
  export interface ITelemetryEvaluationProducerAsync {
442
432
  recordLatency(method: Method, latencyMs: number): Promise<any>;
443
433
  recordException(method: Method): Promise<any>;
434
+ recordConfig(): Promise<any>;
444
435
  }
445
436
 
446
437
  // ATM it only implements the producer API, used by the SDK in consumer mode.
447
- // @TODO implement consumer API for JS Synchronizer.
448
- export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync { }
438
+ export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync, ITelemetryEvaluationConsumerAsync { }
449
439
 
450
440
  /**
451
441
  * Storages
@@ -455,18 +445,15 @@ export interface IStorageBase<
455
445
  TSplitsCache extends ISplitsCacheBase,
456
446
  TSegmentsCache extends ISegmentsCacheBase,
457
447
  TImpressionsCache extends IImpressionsCacheBase,
458
- TImpressionsCountCache extends IImpressionCountsCacheSync,
459
448
  TEventsCache extends IEventsCacheBase,
460
- TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync,
461
- TUniqueKeysCache extends IUniqueKeysCacheBase
449
+ TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync
462
450
  > {
463
451
  splits: TSplitsCache,
464
452
  segments: TSegmentsCache,
465
453
  impressions: TImpressionsCache,
466
- impressionCounts?: TImpressionsCountCache,
454
+ impressionCounts?: IImpressionCountsCacheSync,
467
455
  events: TEventsCache,
468
- telemetry?: TTelemetryCache,
469
- uniqueKeys?: TUniqueKeysCache,
456
+ telemetry?: TTelemetryCache
470
457
  destroy(): void | Promise<void>,
471
458
  shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this
472
459
  }
@@ -475,20 +462,16 @@ export interface IStorageSync extends IStorageBase<
475
462
  ISplitsCacheSync,
476
463
  ISegmentsCacheSync,
477
464
  IImpressionsCacheSync,
478
- IImpressionCountsCacheSync,
479
465
  IEventsCacheSync,
480
- ITelemetryCacheSync,
481
- IUniqueKeysCacheBase
466
+ ITelemetryCacheSync
482
467
  > { }
483
468
 
484
469
  export interface IStorageAsync extends IStorageBase<
485
470
  ISplitsCacheAsync,
486
471
  ISegmentsCacheAsync,
487
472
  IImpressionsCacheAsync | IImpressionsCacheSync,
488
- IImpressionCountsCacheSync,
489
473
  IEventsCacheAsync | IEventsCacheSync,
490
- ITelemetryCacheAsync,
491
- IUniqueKeysCacheBase
474
+ ITelemetryCacheAsync | ITelemetryCacheSync
492
475
  > { }
493
476
 
494
477
  /** StorageFactory */
@@ -498,14 +481,10 @@ export type DataLoader = (storage: IStorageSync, matchingKey: string) => void
498
481
  export interface IStorageFactoryParams {
499
482
  log: ILogger,
500
483
  impressionsQueueSize?: number,
501
- impressionCountsQueueSize?: number,
502
- impressionCountsRefreshRate?: number,
503
- uniqueKeysRefreshRate?: number,
504
- uniqueKeysCacheSize?: number,
505
484
  eventsQueueSize?: number,
506
485
  optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */,
507
486
  mode: SDKMode,
508
- impressionsMode?: string,
487
+
509
488
  // ATM, only used by InLocalStorage
510
489
  matchingKey?: string, /* undefined on server-side SDKs */
511
490
  splitFiltersValidation?: ISplitFiltersValidation,
@@ -4,7 +4,6 @@ import { impressionCountsSubmitterFactory } from './impressionCountsSubmitter';
4
4
  import { telemetrySubmitterFactory } from './telemetrySubmitter';
5
5
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
6
6
  import { ISubmitterManager } from './types';
7
- import { uniqueKeysSubmitterFactory } from './uniqueKeysSubmitter';
8
7
 
9
8
  export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmitterManager {
10
9
 
@@ -16,7 +15,6 @@ export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmit
16
15
  const impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
17
16
  if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter);
18
17
  const telemetrySubmitter = telemetrySubmitterFactory(params);
19
- if (params.uniqueKeysTracker) submitters.push(uniqueKeysSubmitterFactory(params));
20
18
 
21
19
  return {
22
20
  // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
@@ -1,7 +1,7 @@
1
1
  import { ISegmentsCacheSync, ISplitsCacheSync, ITelemetryCacheSync } from '../../storages/types';
2
2
  import { submitterFactory, firstPushWindowDecorator } from './submitter';
3
3
  import { TelemetryUsageStatsPayload, TelemetryConfigStatsPayload, TelemetryConfigStats } from './types';
4
- import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, NONE, DEBUG_ENUM, OPTIMIZED_ENUM, NONE_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
4
+ import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, DEBUG_ENUM, OPTIMIZED_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
5
5
  import { SDK_READY, SDK_READY_FROM_CACHE } from '../../readiness/constants';
6
6
  import { ConsentStatus, ISettings, SDKMode } from '../../types';
7
7
  import { base } from '../../utils/settingsValidation';
@@ -9,11 +9,12 @@ import { usedKeysMap } from '../../utils/inputValidation/apiKey';
9
9
  import { timer } from '../../utils/timeTracker/timer';
10
10
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
11
11
  import { objectAssign } from '../../utils/lang/objectAssign';
12
+ import { isStorageSync } from '../../trackers/impressionObserver/utils';
12
13
 
13
14
  /**
14
15
  * Converts data from telemetry cache into /metrics/usage request payload.
15
16
  */
16
- export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits: ISplitsCacheSync, segments: ISegmentsCacheSync) {
17
+ export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits?: ISplitsCacheSync, segments?: ISegmentsCacheSync) {
17
18
  return {
18
19
  isEmpty() { return false; }, // There is always data in telemetry cache
19
20
  clear() { }, // No-op
@@ -31,9 +32,9 @@ export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, split
31
32
  iQ: telemetry.getImpressionStats(QUEUED),
32
33
  iDe: telemetry.getImpressionStats(DEDUPED),
33
34
  iDr: telemetry.getImpressionStats(DROPPED),
34
- spC: splits.getSplitNames().length,
35
- seC: segments.getRegisteredSegments().length,
36
- skC: segments.getKeysCount(),
35
+ spC: splits && splits.getSplitNames().length,
36
+ seC: segments && segments.getRegisteredSegments().length,
37
+ skC: segments && segments.getKeysCount(),
37
38
  sL: telemetry.getSessionLength(),
38
39
  eQ: telemetry.getEventStats(QUEUED),
39
40
  eD: telemetry.getEventStats(DROPPED),
@@ -52,9 +53,8 @@ const OPERATION_MODE_MAP = {
52
53
 
53
54
  const IMPRESSIONS_MODE_MAP = {
54
55
  [OPTIMIZED]: OPTIMIZED_ENUM,
55
- [DEBUG]: DEBUG_ENUM,
56
- [NONE]: NONE_ENUM
57
- } as Record<ISettings['sync']['impressionsMode'], (0 | 1 | 2)>;
56
+ [DEBUG]: DEBUG_ENUM
57
+ } as Record<ISettings['sync']['impressionsMode'], (0 | 1)>;
58
58
 
59
59
  const USER_CONSENT_MAP = {
60
60
  [CONSENT_UNKNOWN]: 1,
@@ -137,7 +137,12 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
137
137
  const startTime = timer(now);
138
138
 
139
139
  const submitter = firstPushWindowDecorator(
140
- submitterFactory(log, splitApi.postMetricsUsage, telemetryCacheStatsAdapter(telemetry, splits, segments), telemetryRefreshRate, 'telemetry stats', undefined, 0, true),
140
+ submitterFactory(
141
+ log, splitApi.postMetricsUsage,
142
+ // @TODO cannot provide splits and segments cache if they are async, because `submitterFactory` expects a sync storage source
143
+ isStorageSync(params.settings) ? telemetryCacheStatsAdapter(telemetry, splits, segments) : telemetryCacheStatsAdapter(telemetry),
144
+ telemetryRefreshRate, 'telemetry stats', undefined, 0, true
145
+ ),
141
146
  telemetryRefreshRate
142
147
  );
143
148
 
@@ -1,5 +1,7 @@
1
+ /* eslint-disable no-use-before-define */
1
2
  import { IMetadata } from '../../dtos/types';
2
3
  import { SplitIO } from '../../types';
4
+ import { IMap } from '../../utils/lang/maps';
3
5
  import { ISyncTask } from '../types';
4
6
 
5
7
  export type ImpressionsPayload = {
@@ -35,24 +37,6 @@ export type ImpressionCountsPayload = {
35
37
  }[]
36
38
  }
37
39
 
38
- export type UniqueKeysPayloadSs = {
39
- keys: {
40
- /** Split name */
41
- f: string
42
- /** keyNames */
43
- ks: string[]
44
- }[]
45
- }
46
-
47
- export type UniqueKeysPayloadCs = {
48
- keys: {
49
- /** keyNames */
50
- k: string
51
- /** Split name */
52
- fs: string[]
53
- }[]
54
- }
55
-
56
40
  export type StoredImpressionWithMetadata = {
57
41
  /** Metadata */
58
42
  m: IMetadata,
@@ -82,6 +66,33 @@ export type StoredEventWithMetadata = {
82
66
  e: SplitIO.EventData
83
67
  }
84
68
 
69
+ export type MultiMethodLatencies = IMap<string, MethodLatencies>
70
+
71
+ export type MultiMethodExceptions = IMap<string, MethodExceptions>
72
+
73
+ export type MultiConfigs = IMap<string, TelemetryConfigStats>
74
+
75
+ // export type StoredLatenciesWithMetadata = {
76
+ // /** Metadata */
77
+ // m: IMetadata,
78
+ // /** Stored latencies */
79
+ // l: MethodLatencies
80
+ // }
81
+
82
+ // export type StoredExceptionsWithMetadata = {
83
+ // /** Metadata */
84
+ // m: IMetadata,
85
+ // /** Stored exceptions */
86
+ // e: MethodExceptions
87
+ // }
88
+
89
+ // export type StoredConfigsWithMetadata = {
90
+ // /** Metadata */
91
+ // m: IMetadata,
92
+ // /** Stored configs */
93
+ // c: TelemetryConfigStats
94
+ // }
95
+
85
96
  /**
86
97
  * Telemetry usage stats
87
98
  */
@@ -133,11 +144,15 @@ export type StreamingEvent = {
133
144
  t: number, // timestamp
134
145
  }
135
146
 
147
+ // 'telemetry.latencias' and 'telemetry.exceptions' Redis/Pluggable keys
148
+ export type TelemetryUsageStats = {
149
+ mL?: MethodLatencies, // clientMethodLatencies
150
+ mE?: MethodExceptions, // methodExceptions
151
+ }
152
+
136
153
  // 'metrics/usage' JSON request body
137
- export type TelemetryUsageStatsPayload = {
154
+ export type TelemetryUsageStatsPayload = TelemetryUsageStats & {
138
155
  lS: LastSync, // lastSynchronization
139
- mL: MethodLatencies, // clientMethodLatencies
140
- mE: MethodExceptions, // methodExceptions
141
156
  hE: HttpErrors, // httpErrors
142
157
  hL: HttpLatencies, // httpLatencies
143
158
  tR: number, // tokenRefreshes
@@ -145,9 +160,9 @@ export type TelemetryUsageStatsPayload = {
145
160
  iQ: number, // impressionsQueued
146
161
  iDe: number, // impressionsDeduped
147
162
  iDr: number, // impressionsDropped
148
- spC: number, // splitCount
149
- seC: number, // segmentCount
150
- skC: number, // segmentKeyCount
163
+ spC?: number, // splitCount
164
+ seC?: number, // segmentCount
165
+ skC?: number, // segmentKeyCount
151
166
  sL?: number, // sessionLengthMs
152
167
  eQ: number, // eventsQueued
153
168
  eD: number, // eventsDropped
@@ -166,8 +181,7 @@ export type OperationMode = STANDALONE_ENUM | CONSUMER_ENUM | CONSUMER_PARTIAL_E
166
181
 
167
182
  export type OPTIMIZED_ENUM = 0;
168
183
  export type DEBUG_ENUM = 1;
169
- export type NONE_ENUM = 2;
170
- export type ImpressionsMode = OPTIMIZED_ENUM | DEBUG_ENUM | NONE_ENUM;
184
+ export type ImpressionsMode = OPTIMIZED_ENUM | DEBUG_ENUM;
171
185
 
172
186
  export type RefreshRates = {
173
187
  sp: number, // splits