@splitsoftware/splitio-commons 2.8.1-rc.1 → 2.9.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 (45) hide show
  1. package/CHANGES.txt +3 -2
  2. package/cjs/evaluator/index.js +13 -13
  3. package/cjs/sdkClient/client.js +3 -3
  4. package/cjs/storages/AbstractMySegmentsCacheSync.js +31 -23
  5. package/cjs/storages/AbstractSplitsCacheSync.js +3 -2
  6. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +10 -28
  7. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +22 -33
  8. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +19 -29
  9. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +3 -2
  10. package/cjs/storages/inRedis/SegmentsCacheInRedis.js +1 -1
  11. package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
  12. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  13. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +16 -5
  14. package/cjs/sync/polling/updaters/splitChangesUpdater.js +3 -3
  15. package/cjs/utils/inputValidation/eventProperties.js +1 -6
  16. package/esm/evaluator/index.js +13 -13
  17. package/esm/sdkClient/client.js +3 -3
  18. package/esm/storages/AbstractMySegmentsCacheSync.js +31 -23
  19. package/esm/storages/AbstractSplitsCacheSync.js +3 -2
  20. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +11 -29
  21. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +22 -33
  22. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +19 -29
  23. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +3 -2
  24. package/esm/storages/inRedis/SegmentsCacheInRedis.js +1 -1
  25. package/esm/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
  26. package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  27. package/esm/sync/polling/updaters/segmentChangesUpdater.js +16 -5
  28. package/esm/sync/polling/updaters/splitChangesUpdater.js +3 -3
  29. package/esm/utils/inputValidation/eventProperties.js +1 -6
  30. package/package.json +1 -1
  31. package/src/evaluator/index.ts +6 -14
  32. package/src/sdkClient/client.ts +3 -3
  33. package/src/storages/AbstractMySegmentsCacheSync.ts +26 -20
  34. package/src/storages/AbstractSplitsCacheSync.ts +3 -2
  35. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +9 -24
  36. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +18 -27
  37. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +22 -29
  38. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +3 -2
  39. package/src/storages/inRedis/SegmentsCacheInRedis.ts +1 -1
  40. package/src/sync/polling/syncTasks/segmentsSyncTask.ts +2 -0
  41. package/src/sync/polling/updaters/mySegmentsUpdater.ts +3 -3
  42. package/src/sync/polling/updaters/segmentChangesUpdater.ts +17 -4
  43. package/src/sync/polling/updaters/splitChangesUpdater.ts +6 -7
  44. package/src/utils/inputValidation/eventProperties.ts +1 -6
  45. package/types/splitio.d.ts +0 -10
@@ -30,7 +30,6 @@ export function evaluateFeature(
30
30
  splitName: string,
31
31
  attributes: SplitIO.Attributes | undefined,
32
32
  storage: IStorageSync | IStorageAsync,
33
- options?: SplitIO.EvaluationOptions
34
33
  ): MaybeThenable<IEvaluationResult> {
35
34
  let parsedSplit;
36
35
 
@@ -48,7 +47,6 @@ export function evaluateFeature(
48
47
  split,
49
48
  attributes,
50
49
  storage,
51
- options,
52
50
  )).catch(
53
51
  // Exception on async `getSplit` storage. For example, when the storage is redis or
54
52
  // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
@@ -62,7 +60,6 @@ export function evaluateFeature(
62
60
  parsedSplit,
63
61
  attributes,
64
62
  storage,
65
- options,
66
63
  );
67
64
  }
68
65
 
@@ -72,7 +69,6 @@ export function evaluateFeatures(
72
69
  splitNames: string[],
73
70
  attributes: SplitIO.Attributes | undefined,
74
71
  storage: IStorageSync | IStorageAsync,
75
- options?: SplitIO.EvaluationOptions,
76
72
  ): MaybeThenable<Record<string, IEvaluationResult>> {
77
73
  let parsedSplits;
78
74
 
@@ -84,13 +80,13 @@ export function evaluateFeatures(
84
80
  }
85
81
 
86
82
  return thenable(parsedSplits) ?
87
- parsedSplits.then(splits => getEvaluations(log, key, splitNames, splits, attributes, storage, options))
83
+ parsedSplits.then(splits => getEvaluations(log, key, splitNames, splits, attributes, storage))
88
84
  .catch(() => {
89
85
  // Exception on async `getSplits` storage. For example, when the storage is redis or
90
86
  // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
91
87
  return treatmentsException(splitNames);
92
88
  }) :
93
- getEvaluations(log, key, splitNames, parsedSplits, attributes, storage, options);
89
+ getEvaluations(log, key, splitNames, parsedSplits, attributes, storage);
94
90
  }
95
91
 
96
92
  export function evaluateFeaturesByFlagSets(
@@ -100,7 +96,6 @@ export function evaluateFeaturesByFlagSets(
100
96
  attributes: SplitIO.Attributes | undefined,
101
97
  storage: IStorageSync | IStorageAsync,
102
98
  method: string,
103
- options?: SplitIO.EvaluationOptions,
104
99
  ): MaybeThenable<Record<string, IEvaluationResult>> {
105
100
  let storedFlagNames: MaybeThenable<Set<string>[]>;
106
101
 
@@ -116,7 +111,7 @@ export function evaluateFeaturesByFlagSets(
116
111
  }
117
112
 
118
113
  return featureFlags.size ?
119
- evaluateFeatures(log, key, setToArray(featureFlags), attributes, storage, options) :
114
+ evaluateFeatures(log, key, setToArray(featureFlags), attributes, storage) :
120
115
  {};
121
116
  }
122
117
 
@@ -143,7 +138,6 @@ function getEvaluation(
143
138
  splitJSON: ISplit | null,
144
139
  attributes: SplitIO.Attributes | undefined,
145
140
  storage: IStorageSync | IStorageAsync,
146
- options?: SplitIO.EvaluationOptions,
147
141
  ): MaybeThenable<IEvaluationResult> {
148
142
  let evaluation: MaybeThenable<IEvaluationResult> = {
149
143
  treatment: CONTROL,
@@ -160,14 +154,14 @@ function getEvaluation(
160
154
  return evaluation.then(result => {
161
155
  result.changeNumber = splitJSON.changeNumber;
162
156
  result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
163
- result.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled;
157
+ result.impressionsDisabled = splitJSON.impressionsDisabled;
164
158
 
165
159
  return result;
166
160
  });
167
161
  } else {
168
162
  evaluation.changeNumber = splitJSON.changeNumber;
169
163
  evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
170
- evaluation.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled;
164
+ evaluation.impressionsDisabled = splitJSON.impressionsDisabled;
171
165
  }
172
166
  }
173
167
 
@@ -181,7 +175,6 @@ function getEvaluations(
181
175
  splits: Record<string, ISplit | null>,
182
176
  attributes: SplitIO.Attributes | undefined,
183
177
  storage: IStorageSync | IStorageAsync,
184
- options?: SplitIO.EvaluationOptions,
185
178
  ): MaybeThenable<Record<string, IEvaluationResult>> {
186
179
  const result: Record<string, IEvaluationResult> = {};
187
180
  const thenables: Promise<void>[] = [];
@@ -191,8 +184,7 @@ function getEvaluations(
191
184
  key,
192
185
  splits[splitName],
193
186
  attributes,
194
- storage,
195
- options
187
+ storage
196
188
  );
197
189
  if (thenable(evaluation)) {
198
190
  thenables.push(evaluation.then(res => {
@@ -52,7 +52,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
52
52
  };
53
53
 
54
54
  const evaluation = readinessManager.isReadyFromCache() ?
55
- evaluateFeature(log, key, featureFlagName, attributes, storage, options) :
55
+ evaluateFeature(log, key, featureFlagName, attributes, storage) :
56
56
  isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
57
57
  Promise.resolve(treatmentNotReady) :
58
58
  treatmentNotReady;
@@ -81,7 +81,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
81
81
  };
82
82
 
83
83
  const evaluations = readinessManager.isReadyFromCache() ?
84
- evaluateFeatures(log, key, featureFlagNames, attributes, storage, options) :
84
+ evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
85
85
  isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
86
86
  Promise.resolve(treatmentsNotReady(featureFlagNames)) :
87
87
  treatmentsNotReady(featureFlagNames);
@@ -110,7 +110,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
110
110
  };
111
111
 
112
112
  const evaluations = readinessManager.isReadyFromCache() ?
113
- evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName, options) :
113
+ evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) :
114
114
  isAsync ?
115
115
  Promise.resolve({}) :
116
116
  {};
@@ -49,12 +49,10 @@ export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync
49
49
  * For client-side synchronizer: it resets or updates the cache.
50
50
  */
51
51
  resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean {
52
- this.setChangeNumber(segmentsData.cn);
53
-
52
+ let isDiff = false;
54
53
  const { added, removed } = segmentsData as MySegmentsData;
55
54
 
56
55
  if (added && removed) {
57
- let isDiff = false;
58
56
 
59
57
  added.forEach(segment => {
60
58
  isDiff = this.addSegment(segment) || isDiff;
@@ -63,32 +61,40 @@ export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync
63
61
  removed.forEach(segment => {
64
62
  isDiff = this.removeSegment(segment) || isDiff;
65
63
  });
64
+ } else {
66
65
 
67
- return isDiff;
68
- }
66
+ const names = ((segmentsData as IMySegmentsResponse).k || []).map(s => s.n).sort();
67
+ const storedSegmentKeys = this.getRegisteredSegments().sort();
69
68
 
70
- const names = ((segmentsData as IMySegmentsResponse).k || []).map(s => s.n).sort();
71
- const storedSegmentKeys = this.getRegisteredSegments().sort();
69
+ // Extreme fast => everything is empty
70
+ if (!names.length && !storedSegmentKeys.length) {
71
+ isDiff = false;
72
+ } else {
72
73
 
73
- // Extreme fast => everything is empty
74
- if (!names.length && !storedSegmentKeys.length) return false;
74
+ let index = 0;
75
75
 
76
- let index = 0;
76
+ while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index]) index++;
77
77
 
78
- while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index]) index++;
78
+ // Quick path => no changes
79
+ if (index === names.length && index === storedSegmentKeys.length) {
80
+ isDiff = false;
81
+ } else {
79
82
 
80
- // Quick path => no changes
81
- if (index === names.length && index === storedSegmentKeys.length) return false;
83
+ // Slowest path => add and/or remove segments
84
+ for (let removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
85
+ this.removeSegment(storedSegmentKeys[removeIndex]);
86
+ }
82
87
 
83
- // Slowest path => add and/or remove segments
84
- for (let removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
85
- this.removeSegment(storedSegmentKeys[removeIndex]);
86
- }
88
+ for (let addIndex = index; addIndex < names.length; addIndex++) {
89
+ this.addSegment(names[addIndex]);
90
+ }
87
91
 
88
- for (let addIndex = index; addIndex < names.length; addIndex++) {
89
- this.addSegment(names[addIndex]);
92
+ isDiff = true;
93
+ }
94
+ }
90
95
  }
91
96
 
92
- return true;
97
+ this.setChangeNumber(segmentsData.cn);
98
+ return isDiff;
93
99
  }
94
100
  }
@@ -14,9 +14,10 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
14
14
  protected abstract setChangeNumber(changeNumber: number): boolean | void
15
15
 
16
16
  update(toAdd: ISplit[], toRemove: ISplit[], changeNumber: number): boolean {
17
+ let updated = toAdd.map(addedFF => this.addSplit(addedFF)).some(result => result);
18
+ updated = toRemove.map(removedFF => this.removeSplit(removedFF.name)).some(result => result) || updated;
17
19
  this.setChangeNumber(changeNumber);
18
- const updated = toAdd.map(addedFF => this.addSplit(addedFF)).some(result => result);
19
- return toRemove.map(removedFF => this.removeSplit(removedFF.name)).some(result => result) || updated;
20
+ return updated;
20
21
  }
21
22
 
22
23
  abstract getSplit(name: string): ISplit | null
@@ -2,7 +2,7 @@ import { ILogger } from '../../logger/types';
2
2
  import { isNaNNumber } from '../../utils/lang';
3
3
  import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
4
4
  import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
5
- import { LOG_PREFIX, DEFINED } from './constants';
5
+ import { DEFINED } from './constants';
6
6
  import { StorageAdapter } from '../types';
7
7
 
8
8
  export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
@@ -16,33 +16,22 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
16
16
  this.log = log;
17
17
  this.keys = keys;
18
18
  this.storage = storage;
19
- // There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
20
19
  }
21
20
 
22
21
  protected addSegment(name: string): boolean {
23
22
  const segmentKey = this.keys.buildSegmentNameKey(name);
24
23
 
25
- try {
26
- if (this.storage.getItem(segmentKey) === DEFINED) return false;
27
- this.storage.setItem(segmentKey, DEFINED);
28
- return true;
29
- } catch (e) {
30
- this.log.error(LOG_PREFIX + e);
31
- return false;
32
- }
24
+ if (this.storage.getItem(segmentKey) === DEFINED) return false;
25
+ this.storage.setItem(segmentKey, DEFINED);
26
+ return true;
33
27
  }
34
28
 
35
29
  protected removeSegment(name: string): boolean {
36
30
  const segmentKey = this.keys.buildSegmentNameKey(name);
37
31
 
38
- try {
39
- if (this.storage.getItem(segmentKey) !== DEFINED) return false;
40
- this.storage.removeItem(segmentKey);
41
- return true;
42
- } catch (e) {
43
- this.log.error(LOG_PREFIX + e);
44
- return false;
45
- }
32
+ if (this.storage.getItem(segmentKey) !== DEFINED) return false;
33
+ this.storage.removeItem(segmentKey);
34
+ return true;
46
35
  }
47
36
 
48
37
  isInSegment(name: string): boolean {
@@ -63,12 +52,8 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
63
52
  }
64
53
 
65
54
  protected setChangeNumber(changeNumber?: number) {
66
- try {
67
- if (changeNumber) this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
68
- else this.storage.removeItem(this.keys.buildTillKey());
69
- } catch (e) {
70
- this.log.error(e);
71
- }
55
+ if (changeNumber) this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
56
+ else this.storage.removeItem(this.keys.buildTillKey());
72
57
  }
73
58
 
74
59
  getChangeNumber() {
@@ -26,9 +26,10 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
26
26
  }
27
27
 
28
28
  update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
29
+ let updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
30
+ updated = toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
29
31
  this.setChangeNumber(changeNumber);
30
- const updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
31
- return toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
32
+ return updated;
32
33
  }
33
34
 
34
35
  private setChangeNumber(changeNumber: number) {
@@ -48,40 +49,30 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
48
49
  }
49
50
 
50
51
  private add(rbSegment: IRBSegment): boolean {
51
- try {
52
- const name = rbSegment.name;
53
- const rbSegmentKey = this.keys.buildRBSegmentKey(name);
54
- const rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
55
- const previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
52
+ const name = rbSegment.name;
53
+ const rbSegmentKey = this.keys.buildRBSegmentKey(name);
54
+ const rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
55
+ const previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
56
56
 
57
- this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
57
+ this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
58
58
 
59
- let usesSegmentsDiff = 0;
60
- if (previous && usesSegments(previous)) usesSegmentsDiff--;
61
- if (usesSegments(rbSegment)) usesSegmentsDiff++;
62
- if (usesSegmentsDiff !== 0) this.updateSegmentCount(usesSegmentsDiff);
59
+ let usesSegmentsDiff = 0;
60
+ if (previous && usesSegments(previous)) usesSegmentsDiff--;
61
+ if (usesSegments(rbSegment)) usesSegmentsDiff++;
62
+ if (usesSegmentsDiff !== 0) this.updateSegmentCount(usesSegmentsDiff);
63
63
 
64
- return true;
65
- } catch (e) {
66
- this.log.error(LOG_PREFIX + e);
67
- return false;
68
- }
64
+ return true;
69
65
  }
70
66
 
71
67
  private remove(name: string): boolean {
72
- try {
73
- const rbSegment = this.get(name);
74
- if (!rbSegment) return false;
68
+ const rbSegment = this.get(name);
69
+ if (!rbSegment) return false;
75
70
 
76
- this.storage.removeItem(this.keys.buildRBSegmentKey(name));
71
+ this.storage.removeItem(this.keys.buildRBSegmentKey(name));
77
72
 
78
- if (usesSegments(rbSegment)) this.updateSegmentCount(-1);
73
+ if (usesSegments(rbSegment)) this.updateSegmentCount(-1);
79
74
 
80
- return true;
81
- } catch (e) {
82
- this.log.error(LOG_PREFIX + e);
83
- return false;
84
- }
75
+ return true;
85
76
  }
86
77
 
87
78
  private getNames(): string[] {
@@ -80,44 +80,34 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
80
80
  }
81
81
 
82
82
  addSplit(split: ISplit) {
83
- try {
84
- const name = split.name;
85
- const splitKey = this.keys.buildSplitKey(name);
86
- const splitFromStorage = this.storage.getItem(splitKey);
87
- const previousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : null;
88
-
89
- if (previousSplit) {
90
- this._decrementCounts(previousSplit);
91
- this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
92
- }
83
+ const name = split.name;
84
+ const splitKey = this.keys.buildSplitKey(name);
85
+ const splitFromStorage = this.storage.getItem(splitKey);
86
+ const previousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : null;
87
+
88
+ if (previousSplit) {
89
+ this._decrementCounts(previousSplit);
90
+ this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
91
+ }
93
92
 
94
- this.storage.setItem(splitKey, JSON.stringify(split));
93
+ this.storage.setItem(splitKey, JSON.stringify(split));
95
94
 
96
- this._incrementCounts(split);
97
- this.addToFlagSets(split);
95
+ this._incrementCounts(split);
96
+ this.addToFlagSets(split);
98
97
 
99
- return true;
100
- } catch (e) {
101
- this.log.error(LOG_PREFIX + e);
102
- return false;
103
- }
98
+ return true;
104
99
  }
105
100
 
106
101
  removeSplit(name: string): boolean {
107
- try {
108
- const split = this.getSplit(name);
109
- if (!split) return false;
102
+ const split = this.getSplit(name);
103
+ if (!split) return false;
110
104
 
111
- this.storage.removeItem(this.keys.buildSplitKey(name));
105
+ this.storage.removeItem(this.keys.buildSplitKey(name));
112
106
 
113
- this._decrementCounts(split);
114
- this.removeFromFlagSets(split.name, split.sets);
107
+ this._decrementCounts(split);
108
+ this.removeFromFlagSets(split.name, split.sets);
115
109
 
116
- return true;
117
- } catch (e) {
118
- this.log.error(LOG_PREFIX + e);
119
- return false;
120
- }
110
+ return true;
121
111
  }
122
112
 
123
113
  getSplit(name: string): ISplit | null {
@@ -206,6 +196,9 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
206
196
  const flagSetFromStorage = this.storage.getItem(flagSetKey);
207
197
 
208
198
  const flagSetCache = new Set(flagSetFromStorage ? JSON.parse(flagSetFromStorage) : []);
199
+
200
+ if (flagSetCache.has(featureFlag.name)) return;
201
+
209
202
  flagSetCache.add(featureFlag.name);
210
203
 
211
204
  this.storage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
@@ -16,9 +16,10 @@ export class RBSegmentsCacheInMemory implements IRBSegmentsCacheSync {
16
16
  }
17
17
 
18
18
  update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
19
+ let updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
20
+ updated = toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
19
21
  this.changeNumber = changeNumber;
20
- const updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
21
- return toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
22
+ return updated;
22
23
  }
23
24
 
24
25
  private add(rbSegment: IRBSegment): boolean {
@@ -1,6 +1,6 @@
1
1
  import { ILogger } from '../../logger/types';
2
2
  import { isNaNNumber } from '../../utils/lang';
3
- import { LOG_PREFIX } from '../inLocalStorage/constants';
3
+ import { LOG_PREFIX } from './constants';
4
4
  import { KeyBuilderSS } from '../KeyBuilderSS';
5
5
  import { ISegmentsCacheAsync } from '../types';
6
6
  import type { RedisAdapter } from './RedisAdapter';
@@ -23,6 +23,8 @@ export function segmentsSyncTaskFactory(
23
23
  segmentChangesFetcherFactory(fetchSegmentChanges),
24
24
  storage.segments,
25
25
  readiness,
26
+ settings.startup.requestTimeoutBeforeReady,
27
+ settings.startup.retriesOnFailureBeforeReady,
26
28
  ),
27
29
  settings.scheduler.segmentsRefreshRate,
28
30
  'segmentChangesUpdater'
@@ -66,10 +66,10 @@ export function mySegmentsUpdaterFactory(
66
66
  new Promise((res) => { updateSegments(segmentsData); res(true); }) :
67
67
  // If not provided, fetch mySegments
68
68
  mySegmentsFetcher(matchingKey, noCache, till, _promiseDecorator).then(segments => {
69
- // Only when we have downloaded segments completely, we should not keep retrying anymore
70
- startingUp = false;
71
-
72
69
  updateSegments(segments);
70
+
71
+ // Only when we have downloaded and stored segments completely, we should not keep retrying anymore
72
+ startingUp = false;
73
73
  return true;
74
74
  });
75
75
 
@@ -4,6 +4,7 @@ import { IReadinessManager } from '../../../readiness/types';
4
4
  import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
5
5
  import { ILogger } from '../../../logger/types';
6
6
  import { LOG_PREFIX_INSTANTIATION, LOG_PREFIX_SYNC_SEGMENTS } from '../../../logger/constants';
7
+ import { timeout } from '../../../utils/promise/timeout';
7
8
 
8
9
  type ISegmentChangesUpdater = (fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number) => Promise<boolean>
9
10
 
@@ -23,11 +24,18 @@ export function segmentChangesUpdaterFactory(
23
24
  segmentChangesFetcher: ISegmentChangesFetcher,
24
25
  segments: ISegmentsCacheBase,
25
26
  readiness?: IReadinessManager,
27
+ requestTimeoutBeforeReady?: number,
28
+ retriesOnFailureBeforeReady?: number,
26
29
  ): ISegmentChangesUpdater {
27
30
 
28
31
  let readyOnAlreadyExistentState = true;
29
32
 
30
- function updateSegment(segmentName: string, noCache?: boolean, till?: number, fetchOnlyNew?: boolean): Promise<boolean> {
33
+ function _promiseDecorator<T>(promise: Promise<T>) {
34
+ if (readyOnAlreadyExistentState && requestTimeoutBeforeReady) promise = timeout(requestTimeoutBeforeReady, promise);
35
+ return promise;
36
+ }
37
+
38
+ function updateSegment(segmentName: string, noCache?: boolean, till?: number, fetchOnlyNew?: boolean, retries?: number): Promise<boolean> {
31
39
  log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing segment ${segmentName}`);
32
40
  let sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
33
41
 
@@ -35,13 +43,19 @@ export function segmentChangesUpdaterFactory(
35
43
  // if fetchOnlyNew flag, avoid processing already fetched segments
36
44
  return fetchOnlyNew && since !== undefined ?
37
45
  false :
38
- segmentChangesFetcher(since || -1, segmentName, noCache, till).then((changes) => {
46
+ segmentChangesFetcher(since || -1, segmentName, noCache, till, _promiseDecorator).then((changes) => {
39
47
  return Promise.all(changes.map(x => {
40
48
  log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing ${segmentName} with till = ${x.till}. Added: ${x.added.length}. Removed: ${x.removed.length}`);
41
49
  return segments.update(segmentName, x.added, x.removed, x.till);
42
50
  })).then((updates) => {
43
51
  return updates.some(update => update);
44
52
  });
53
+ }).catch(error => {
54
+ if (retries) {
55
+ log.warn(`${LOG_PREFIX_SYNC_SEGMENTS}Retrying fetch of segment ${segmentName} (attempt #${retries}). Reason: ${error}`);
56
+ return updateSegment(segmentName, noCache, till, fetchOnlyNew, retries - 1);
57
+ }
58
+ throw error;
45
59
  });
46
60
  });
47
61
  }
@@ -63,8 +77,7 @@ export function segmentChangesUpdaterFactory(
63
77
  let segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments());
64
78
 
65
79
  return segmentsPromise.then(segmentNames => {
66
- // Async fetchers
67
- const updaters = segmentNames.map(segmentName => updateSegment(segmentName, noCache, till, fetchOnlyNew));
80
+ const updaters = segmentNames.map(segmentName => updateSegment(segmentName, noCache, till, fetchOnlyNew, readyOnAlreadyExistentState ? retriesOnFailureBeforeReady : 0));
68
81
 
69
82
  return Promise.all(updaters).then(shouldUpdateFlags => {
70
83
  // if at least one segment fetch succeeded, mark segments ready
@@ -120,8 +120,8 @@ export function splitChangesUpdaterFactory(
120
120
  storage: Pick<IStorageBase, 'splits' | 'rbSegments' | 'segments' | 'save'>,
121
121
  splitFiltersValidation: ISplitFiltersValidation,
122
122
  splitsEventEmitter?: ISplitsEventEmitter,
123
- requestTimeoutBeforeReady: number = 0,
124
- retriesOnFailureBeforeReady: number = 0,
123
+ requestTimeoutBeforeReady = 0,
124
+ retriesOnFailureBeforeReady = 0,
125
125
  isClientSide?: boolean
126
126
  ): SplitChangesUpdater {
127
127
  const { splits, rbSegments, segments } = storage;
@@ -163,8 +163,6 @@ export function splitChangesUpdaterFactory(
163
163
  splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator)
164
164
  )
165
165
  .then((splitChanges: ISplitChangesResponse) => {
166
- startingUp = false;
167
-
168
166
  const usedSegments = new Set<string>();
169
167
 
170
168
  let ffUpdate: MaybeThenable<boolean> = false;
@@ -187,6 +185,8 @@ export function splitChangesUpdaterFactory(
187
185
  ]).then(([ffChanged, rbsChanged]) => {
188
186
  if (storage.save) storage.save();
189
187
 
188
+ startingUp = false;
189
+
190
190
  if (splitsEventEmitter) {
191
191
  // To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
192
192
  return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
@@ -201,14 +201,13 @@ export function splitChangesUpdaterFactory(
201
201
  });
202
202
  })
203
203
  .catch(error => {
204
- log.warn(SYNC_SPLITS_FETCH_FAILS, [error]);
205
-
206
204
  if (startingUp && retriesOnFailureBeforeReady > retry) {
207
205
  retry += 1;
208
- log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
206
+ log.warn(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
209
207
  return _splitChangesUpdater(sinces, retry);
210
208
  } else {
211
209
  startingUp = false;
210
+ log.warn(SYNC_SPLITS_FETCH_FAILS, [error]);
212
211
  }
213
212
  return false;
214
213
  });
@@ -70,12 +70,7 @@ export function validateEventProperties(log: ILogger, maybeProperties: any, meth
70
70
  export function validateEvaluationOptions(log: ILogger, maybeOptions: any, method: string): SplitIO.EvaluationOptions | undefined {
71
71
  if (isObject(maybeOptions)) {
72
72
  const properties = validateEventProperties(log, maybeOptions.properties, method).properties;
73
- let options = properties && Object.keys(properties).length > 0 ? { properties } : undefined;
74
-
75
- const impressionsDisabled = maybeOptions.impressionsDisabled;
76
- if (!impressionsDisabled) return options;
77
-
78
- return options ? { ...options, impressionsDisabled } : { impressionsDisabled };
73
+ return properties && Object.keys(properties).length > 0 ? { properties } : undefined;
79
74
  } else if (maybeOptions) {
80
75
  log.error(ERROR_NOT_PLAIN_OBJECT, [method, 'evaluation options']);
81
76
  }
@@ -918,16 +918,8 @@ declare namespace SplitIO {
918
918
  * Evaluation options object for getTreatment methods.
919
919
  */
920
920
  type EvaluationOptions = {
921
- /**
922
- * Whether the evaluation/s will track impressions or not.
923
- *
924
- * @defaultValue `false`
925
- */
926
- impressionsDisabled?: boolean;
927
921
  /**
928
922
  * Optional properties to append to the generated impression object sent to Split backend.
929
- *
930
- * @defaultValue `undefined`
931
923
  */
932
924
  properties?: Properties;
933
925
  }
@@ -1343,8 +1335,6 @@ declare namespace SplitIO {
1343
1335
  /**
1344
1336
  * Defines the factory function to instantiate the storage. If not provided, the default in-memory storage is used.
1345
1337
  *
1346
- * NOTE: Currently, there is no persistent storage option available for the React Native SDK; only `InLocalStorage` for the Browser SDK.
1347
- *
1348
1338
  * Example:
1349
1339
  * ```
1350
1340
  * SplitFactory({