@splitsoftware/splitio-commons 2.1.0 → 2.1.1-rc.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 (154) hide show
  1. package/README.md +1 -0
  2. package/cjs/evaluator/combiners/and.js +2 -6
  3. package/cjs/evaluator/combiners/ifelseif.js +6 -6
  4. package/cjs/evaluator/condition/index.js +6 -5
  5. package/cjs/evaluator/index.js +7 -7
  6. package/cjs/evaluator/matchers/index.js +3 -1
  7. package/cjs/evaluator/matchers/matcherTypes.js +1 -0
  8. package/cjs/evaluator/matchers/rbsegment.js +43 -0
  9. package/cjs/evaluator/matchersTransform/index.js +4 -0
  10. package/cjs/evaluator/parser/index.js +2 -2
  11. package/cjs/evaluator/value/sanitize.js +1 -0
  12. package/cjs/logger/constants.js +5 -6
  13. package/cjs/logger/messages/debug.js +3 -4
  14. package/cjs/logger/messages/warn.js +1 -1
  15. package/cjs/services/splitApi.js +2 -2
  16. package/cjs/storages/AbstractSplitsCacheAsync.js +12 -1
  17. package/cjs/storages/AbstractSplitsCacheSync.js +10 -9
  18. package/cjs/storages/KeyBuilder.js +8 -15
  19. package/cjs/storages/KeyBuilderCS.js +12 -3
  20. package/cjs/storages/KeyBuilderSS.js +3 -0
  21. package/cjs/storages/dataLoader.js +1 -2
  22. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +117 -0
  23. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +14 -16
  24. package/cjs/storages/inLocalStorage/index.js +4 -0
  25. package/cjs/storages/inMemory/InMemoryStorage.js +3 -0
  26. package/cjs/storages/inMemory/InMemoryStorageCS.js +4 -0
  27. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +61 -0
  28. package/cjs/storages/inMemory/SplitsCacheInMemory.js +24 -31
  29. package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +64 -0
  30. package/cjs/storages/inRedis/SplitsCacheInRedis.js +4 -21
  31. package/cjs/storages/inRedis/index.js +2 -0
  32. package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +64 -0
  33. package/cjs/storages/pluggable/SplitsCachePluggable.js +2 -19
  34. package/cjs/storages/pluggable/index.js +2 -0
  35. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +12 -13
  36. package/cjs/sync/polling/fetchers/splitChangesFetcher.js +2 -2
  37. package/cjs/sync/polling/pollingManagerCS.js +7 -7
  38. package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
  39. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  40. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  41. package/cjs/sync/polling/updaters/splitChangesUpdater.js +53 -51
  42. package/cjs/sync/streaming/SSEHandler/index.js +1 -0
  43. package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +106 -77
  44. package/cjs/sync/streaming/constants.js +2 -1
  45. package/cjs/sync/streaming/pushManager.js +3 -16
  46. package/cjs/sync/syncManagerOnline.js +2 -2
  47. package/cjs/utils/constants/index.js +3 -2
  48. package/esm/evaluator/combiners/and.js +2 -6
  49. package/esm/evaluator/combiners/ifelseif.js +7 -7
  50. package/esm/evaluator/condition/index.js +6 -5
  51. package/esm/evaluator/index.js +7 -7
  52. package/esm/evaluator/matchers/index.js +3 -1
  53. package/esm/evaluator/matchers/matcherTypes.js +1 -0
  54. package/esm/evaluator/matchers/rbsegment.js +39 -0
  55. package/esm/evaluator/matchersTransform/index.js +4 -0
  56. package/esm/evaluator/parser/index.js +2 -2
  57. package/esm/evaluator/value/sanitize.js +1 -0
  58. package/esm/logger/constants.js +2 -3
  59. package/esm/logger/messages/debug.js +3 -4
  60. package/esm/logger/messages/warn.js +1 -1
  61. package/esm/services/splitApi.js +2 -2
  62. package/esm/storages/AbstractSplitsCacheAsync.js +12 -1
  63. package/esm/storages/AbstractSplitsCacheSync.js +10 -9
  64. package/esm/storages/KeyBuilder.js +8 -15
  65. package/esm/storages/KeyBuilderCS.js +12 -3
  66. package/esm/storages/KeyBuilderSS.js +3 -0
  67. package/esm/storages/dataLoader.js +1 -2
  68. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +114 -0
  69. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +14 -16
  70. package/esm/storages/inLocalStorage/index.js +4 -0
  71. package/esm/storages/inMemory/InMemoryStorage.js +3 -0
  72. package/esm/storages/inMemory/InMemoryStorageCS.js +4 -0
  73. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +58 -0
  74. package/esm/storages/inMemory/SplitsCacheInMemory.js +24 -31
  75. package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +61 -0
  76. package/esm/storages/inRedis/SplitsCacheInRedis.js +4 -21
  77. package/esm/storages/inRedis/index.js +2 -0
  78. package/esm/storages/pluggable/RBSegmentsCachePluggable.js +61 -0
  79. package/esm/storages/pluggable/SplitsCachePluggable.js +2 -19
  80. package/esm/storages/pluggable/index.js +2 -0
  81. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +12 -13
  82. package/esm/sync/polling/fetchers/splitChangesFetcher.js +2 -2
  83. package/esm/sync/polling/pollingManagerCS.js +7 -7
  84. package/esm/sync/polling/syncTasks/splitsSyncTask.js +1 -1
  85. package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  86. package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  87. package/esm/sync/polling/updaters/splitChangesUpdater.js +53 -51
  88. package/esm/sync/streaming/SSEHandler/index.js +2 -1
  89. package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +102 -73
  90. package/esm/sync/streaming/constants.js +1 -0
  91. package/esm/sync/streaming/pushManager.js +6 -19
  92. package/esm/sync/syncManagerOnline.js +2 -2
  93. package/esm/utils/constants/index.js +2 -1
  94. package/package.json +1 -1
  95. package/src/dtos/types.ts +32 -8
  96. package/src/evaluator/Engine.ts +1 -1
  97. package/src/evaluator/combiners/and.ts +5 -4
  98. package/src/evaluator/combiners/ifelseif.ts +7 -9
  99. package/src/evaluator/condition/engineUtils.ts +1 -1
  100. package/src/evaluator/condition/index.ts +12 -12
  101. package/src/evaluator/index.ts +7 -7
  102. package/src/evaluator/matchers/index.ts +3 -1
  103. package/src/evaluator/matchers/matcherTypes.ts +1 -0
  104. package/src/evaluator/matchers/rbsegment.ts +61 -0
  105. package/src/evaluator/matchersTransform/index.ts +3 -0
  106. package/src/evaluator/parser/index.ts +3 -3
  107. package/src/evaluator/types.ts +2 -2
  108. package/src/evaluator/value/index.ts +2 -2
  109. package/src/evaluator/value/sanitize.ts +5 -4
  110. package/src/logger/constants.ts +2 -3
  111. package/src/logger/messages/debug.ts +3 -4
  112. package/src/logger/messages/warn.ts +1 -1
  113. package/src/sdkManager/index.ts +1 -1
  114. package/src/services/splitApi.ts +2 -2
  115. package/src/services/types.ts +1 -1
  116. package/src/storages/AbstractSplitsCacheAsync.ts +15 -5
  117. package/src/storages/AbstractSplitsCacheSync.ts +14 -15
  118. package/src/storages/KeyBuilder.ts +9 -17
  119. package/src/storages/KeyBuilderCS.ts +15 -4
  120. package/src/storages/KeyBuilderSS.ts +4 -0
  121. package/src/storages/dataLoader.ts +1 -2
  122. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +136 -0
  123. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +15 -16
  124. package/src/storages/inLocalStorage/index.ts +4 -0
  125. package/src/storages/inMemory/InMemoryStorage.ts +3 -0
  126. package/src/storages/inMemory/InMemoryStorageCS.ts +4 -0
  127. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +68 -0
  128. package/src/storages/inMemory/SplitsCacheInMemory.ts +22 -27
  129. package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +79 -0
  130. package/src/storages/inRedis/SplitsCacheInRedis.ts +4 -21
  131. package/src/storages/inRedis/index.ts +2 -0
  132. package/src/storages/pluggable/RBSegmentsCachePluggable.ts +76 -0
  133. package/src/storages/pluggable/SplitsCachePluggable.ts +2 -19
  134. package/src/storages/pluggable/index.ts +2 -0
  135. package/src/storages/types.ts +43 -17
  136. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +14 -15
  137. package/src/sync/polling/fetchers/splitChangesFetcher.ts +2 -1
  138. package/src/sync/polling/fetchers/types.ts +1 -0
  139. package/src/sync/polling/pollingManagerCS.ts +7 -7
  140. package/src/sync/polling/syncTasks/splitsSyncTask.ts +1 -2
  141. package/src/sync/polling/types.ts +2 -2
  142. package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -2
  143. package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
  144. package/src/sync/polling/updaters/splitChangesUpdater.ts +64 -62
  145. package/src/sync/streaming/SSEHandler/index.ts +2 -1
  146. package/src/sync/streaming/SSEHandler/types.ts +2 -2
  147. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +98 -68
  148. package/src/sync/streaming/constants.ts +1 -0
  149. package/src/sync/streaming/parseUtils.ts +2 -2
  150. package/src/sync/streaming/pushManager.ts +6 -18
  151. package/src/sync/streaming/types.ts +3 -2
  152. package/src/sync/syncManagerOnline.ts +2 -2
  153. package/src/utils/constants/index.ts +2 -1
  154. package/src/utils/lang/index.ts +1 -1
@@ -57,16 +57,14 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
57
57
 
58
58
  private _incrementCounts(split: ISplit) {
59
59
  try {
60
- if (split) {
61
- const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
62
- // @ts-expect-error
63
- localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
60
+ const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
61
+ // @ts-expect-error
62
+ localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
64
63
 
65
- if (usesSegments(split)) {
66
- const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
67
- // @ts-expect-error
68
- localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
69
- }
64
+ if (usesSegments(split)) {
65
+ const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
66
+ // @ts-expect-error
67
+ localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
70
68
  }
71
69
  } catch (e) {
72
70
  this.log.error(LOG_PREFIX + e);
@@ -96,8 +94,9 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
96
94
  this.hasSync = false;
97
95
  }
98
96
 
99
- addSplit(name: string, split: ISplit) {
97
+ addSplit(split: ISplit) {
100
98
  try {
99
+ const name = split.name;
101
100
  const splitKey = this.keys.buildSplitKey(name);
102
101
  const splitFromLocalStorage = localStorage.getItem(splitKey);
103
102
  const previousSplit = splitFromLocalStorage ? JSON.parse(splitFromLocalStorage) : null;
@@ -120,6 +119,8 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
120
119
  removeSplit(name: string): boolean {
121
120
  try {
122
121
  const split = this.getSplit(name);
122
+ if (!split) return false;
123
+
123
124
  localStorage.removeItem(this.keys.buildSplitKey(name));
124
125
 
125
126
  this._decrementCounts(split);
@@ -132,7 +133,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
132
133
  }
133
134
  }
134
135
 
135
- getSplit(name: string) {
136
+ getSplit(name: string): ISplit | null {
136
137
  const item = localStorage.getItem(this.keys.buildSplitKey(name));
137
138
  return item && JSON.parse(item);
138
139
  }
@@ -205,11 +206,9 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
205
206
  const storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
206
207
  const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
207
208
 
208
- if (isFiniteNumber(splitsWithSegmentsCount)) {
209
- return splitsWithSegmentsCount > 0;
210
- } else {
211
- return true;
212
- }
209
+ return isFiniteNumber(splitsWithSegmentsCount) ?
210
+ splitsWithSegmentsCount > 0 :
211
+ true;
213
212
  }
214
213
 
215
214
  /**
@@ -14,6 +14,7 @@ import { STORAGE_LOCALSTORAGE } from '../../utils/constants';
14
14
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
15
15
  import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
16
16
  import { getMatching } from '../../utils/key';
17
+ import { RBSegmentsCacheInLocal } from './RBSegmentsCacheInLocal';
17
18
 
18
19
  export interface InLocalStorageOptions {
19
20
  prefix?: string
@@ -40,11 +41,13 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
40
41
  const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
41
42
 
42
43
  const splits = new SplitsCacheInLocal(settings, keys, expirationTimestamp);
44
+ const rbSegments = new RBSegmentsCacheInLocal(settings, keys);
43
45
  const segments = new MySegmentsCacheInLocal(log, keys);
44
46
  const largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey));
45
47
 
46
48
  return {
47
49
  splits,
50
+ rbSegments,
48
51
  segments,
49
52
  largeSegments,
50
53
  impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
@@ -60,6 +63,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
60
63
 
61
64
  return {
62
65
  splits: this.splits,
66
+ rbSegments: this.rbSegments,
63
67
  segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey)),
64
68
  largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey)),
65
69
  impressions: this.impressions,
@@ -7,6 +7,7 @@ import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
7
  import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
8
8
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
9
  import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
10
+ import { RBSegmentsCacheInMemory } from './RBSegmentsCacheInMemory';
10
11
 
11
12
  /**
12
13
  * InMemory storage factory for standalone server-side SplitFactory
@@ -17,10 +18,12 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
17
18
  const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { __splitFiltersValidation } } } = params;
18
19
 
19
20
  const splits = new SplitsCacheInMemory(__splitFiltersValidation);
21
+ const rbSegments = new RBSegmentsCacheInMemory();
20
22
  const segments = new SegmentsCacheInMemory();
21
23
 
22
24
  const storage = {
23
25
  splits,
26
+ rbSegments,
24
27
  segments,
25
28
  impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
26
29
  impressionCounts: new ImpressionCountsCacheInMemory(),
@@ -7,6 +7,7 @@ import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
7
  import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
8
8
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
9
  import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
10
+ import { RBSegmentsCacheInMemory } from './RBSegmentsCacheInMemory';
10
11
 
11
12
  /**
12
13
  * InMemory storage factory for standalone client-side SplitFactory
@@ -17,11 +18,13 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
17
18
  const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize }, sync: { __splitFiltersValidation } } } = params;
18
19
 
19
20
  const splits = new SplitsCacheInMemory(__splitFiltersValidation);
21
+ const rbSegments = new RBSegmentsCacheInMemory();
20
22
  const segments = new MySegmentsCacheInMemory();
21
23
  const largeSegments = new MySegmentsCacheInMemory();
22
24
 
23
25
  const storage = {
24
26
  splits,
27
+ rbSegments,
25
28
  segments,
26
29
  largeSegments,
27
30
  impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
@@ -36,6 +39,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
36
39
  shared() {
37
40
  return {
38
41
  splits: this.splits,
42
+ rbSegments: this.rbSegments,
39
43
  segments: new MySegmentsCacheInMemory(),
40
44
  largeSegments: new MySegmentsCacheInMemory(),
41
45
  impressions: this.impressions,
@@ -0,0 +1,68 @@
1
+ import { IRBSegment } from '../../dtos/types';
2
+ import { setToArray } from '../../utils/lang/sets';
3
+ import { usesSegments } from '../AbstractSplitsCacheSync';
4
+ import { IRBSegmentsCacheSync } from '../types';
5
+
6
+ export class RBSegmentsCacheInMemory implements IRBSegmentsCacheSync {
7
+
8
+ private cache: Record<string, IRBSegment> = {};
9
+ private changeNumber: number = -1;
10
+ private segmentsCount: number = 0;
11
+
12
+ clear() {
13
+ this.cache = {};
14
+ this.changeNumber = -1;
15
+ this.segmentsCount = 0;
16
+ }
17
+
18
+ update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
19
+ 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
+ }
23
+
24
+ private add(rbSegment: IRBSegment): boolean {
25
+ const name = rbSegment.name;
26
+ const previous = this.get(name);
27
+ if (previous && usesSegments(previous)) this.segmentsCount--;
28
+
29
+ this.cache[name] = rbSegment;
30
+ if (usesSegments(rbSegment)) this.segmentsCount++;
31
+
32
+ return true;
33
+ }
34
+
35
+ private remove(name: string): boolean {
36
+ const rbSegment = this.get(name);
37
+ if (!rbSegment) return false;
38
+
39
+ delete this.cache[name];
40
+
41
+ if (usesSegments(rbSegment)) this.segmentsCount--;
42
+
43
+ return true;
44
+ }
45
+
46
+ private getNames(): string[] {
47
+ return Object.keys(this.cache);
48
+ }
49
+
50
+ get(name: string): IRBSegment | null {
51
+ return this.cache[name] || null;
52
+ }
53
+
54
+ contains(names: Set<string>): boolean {
55
+ const namesArray = setToArray(names);
56
+ const namesInStorage = this.getNames();
57
+ return namesArray.every(name => namesInStorage.indexOf(name) !== -1);
58
+ }
59
+
60
+ getChangeNumber(): number {
61
+ return this.changeNumber;
62
+ }
63
+
64
+ usesSegments(): boolean {
65
+ return this.segmentsCount > 0;
66
+ }
67
+
68
+ }
@@ -26,7 +26,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
26
26
  this.segmentsCount = 0;
27
27
  }
28
28
 
29
- addSplit(name: string, split: ISplit): boolean {
29
+ addSplit(split: ISplit): boolean {
30
+ const name = split.name;
30
31
  const previousSplit = this.getSplit(name);
31
32
  if (previousSplit) { // We had this Split already
32
33
 
@@ -40,41 +41,35 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
40
41
  if (usesSegments(previousSplit)) this.segmentsCount--;
41
42
  }
42
43
 
43
- if (split) {
44
- // Store the Split.
45
- this.splitsCache[name] = split;
46
- // Update TT cache
47
- const ttName = split.trafficTypeName;
48
- this.ttCache[ttName] = (this.ttCache[ttName] || 0) + 1;
49
- this.addToFlagSets(split);
44
+ // Store the Split.
45
+ this.splitsCache[name] = split;
46
+ // Update TT cache
47
+ const ttName = split.trafficTypeName;
48
+ this.ttCache[ttName] = (this.ttCache[ttName] || 0) + 1;
49
+ this.addToFlagSets(split);
50
50
 
51
- // Add to segments count for the new version of the Split
52
- if (usesSegments(split)) this.segmentsCount++;
51
+ // Add to segments count for the new version of the Split
52
+ if (usesSegments(split)) this.segmentsCount++;
53
53
 
54
- return true;
55
- } else {
56
- return false;
57
- }
54
+ return true;
58
55
  }
59
56
 
60
57
  removeSplit(name: string): boolean {
61
58
  const split = this.getSplit(name);
62
- if (split) {
63
- // Delete the Split
64
- delete this.splitsCache[name];
59
+ if (!split) return false;
65
60
 
66
- const ttName = split.trafficTypeName;
67
- this.ttCache[ttName]--; // Update tt cache
68
- if (!this.ttCache[ttName]) delete this.ttCache[ttName];
69
- this.removeFromFlagSets(split.name, split.sets);
61
+ // Delete the Split
62
+ delete this.splitsCache[name];
70
63
 
71
- // Update the segments count.
72
- if (usesSegments(split)) this.segmentsCount--;
64
+ const ttName = split.trafficTypeName;
65
+ this.ttCache[ttName]--; // Update tt cache
66
+ if (!this.ttCache[ttName]) delete this.ttCache[ttName];
67
+ this.removeFromFlagSets(split.name, split.sets);
73
68
 
74
- return true;
75
- } else {
76
- return false;
77
- }
69
+ // Update the segments count.
70
+ if (usesSegments(split)) this.segmentsCount--;
71
+
72
+ return true;
78
73
  }
79
74
 
80
75
  getSplit(name: string): ISplit | null {
@@ -0,0 +1,79 @@
1
+ import { isNaNNumber } from '../../utils/lang';
2
+ import { IRBSegmentsCacheAsync } from '../types';
3
+ import { ILogger } from '../../logger/types';
4
+ import { IRBSegment } from '../../dtos/types';
5
+ import { LOG_PREFIX } from './constants';
6
+ import { setToArray } from '../../utils/lang/sets';
7
+ import { RedisAdapter } from './RedisAdapter';
8
+ import { KeyBuilderSS } from '../KeyBuilderSS';
9
+
10
+ export class RBSegmentsCacheInRedis implements IRBSegmentsCacheAsync {
11
+
12
+ private readonly log: ILogger;
13
+ private readonly keys: KeyBuilderSS;
14
+ private readonly redis: RedisAdapter;
15
+
16
+ constructor(log: ILogger, keys: KeyBuilderSS, redis: RedisAdapter) {
17
+ this.log = log;
18
+ this.keys = keys;
19
+ this.redis = redis;
20
+ }
21
+
22
+ get(name: string): Promise<IRBSegment | null> {
23
+ return this.redis.get(this.keys.buildRBSegmentKey(name))
24
+ .then(maybeRBSegment => maybeRBSegment && JSON.parse(maybeRBSegment));
25
+ }
26
+
27
+ private getNames(): Promise<string[]> {
28
+ return this.redis.keys(this.keys.searchPatternForRBSegmentKeys()).then(
29
+ (listOfKeys) => listOfKeys.map(this.keys.extractKey)
30
+ );
31
+ }
32
+
33
+ contains(names: Set<string>): Promise<boolean> {
34
+ const namesArray = setToArray(names);
35
+ return this.getNames().then(namesInStorage => {
36
+ return namesArray.every(name => namesInStorage.includes(name));
37
+ });
38
+ }
39
+
40
+ update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): Promise<boolean> {
41
+ return Promise.all([
42
+ this.setChangeNumber(changeNumber),
43
+ Promise.all(toAdd.map(toAdd => {
44
+ const key = this.keys.buildRBSegmentKey(toAdd.name);
45
+ const stringifiedNewRBSegment = JSON.stringify(toAdd);
46
+ return this.redis.set(key, stringifiedNewRBSegment).then(() => true);
47
+ })),
48
+ Promise.all(toRemove.map(toRemove => {
49
+ const key = this.keys.buildRBSegmentKey(toRemove.name);
50
+ return this.redis.del(key).then(status => status === 1);
51
+ }))
52
+ ]).then(([, added, removed]) => {
53
+ return added.some(result => result) || removed.some(result => result);
54
+ });
55
+ }
56
+
57
+ setChangeNumber(changeNumber: number) {
58
+ return this.redis.set(this.keys.buildRBSegmentsTillKey(), changeNumber + '').then(
59
+ status => status === 'OK'
60
+ );
61
+ }
62
+
63
+ getChangeNumber(): Promise<number> {
64
+ return this.redis.get(this.keys.buildRBSegmentsTillKey()).then((value: string | null) => {
65
+ const i = parseInt(value as string, 10);
66
+
67
+ return isNaNNumber(i) ? -1 : i;
68
+ }).catch((e) => {
69
+ this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from storage. Error: ' + e);
70
+ return -1;
71
+ });
72
+ }
73
+
74
+ // @TODO implement if required by DataLoader or producer mode
75
+ clear() {
76
+ return Promise.resolve();
77
+ }
78
+
79
+ }
@@ -82,7 +82,8 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
82
82
  * The returned promise is resolved when the operation success
83
83
  * or rejected if it fails (e.g., redis operation fails)
84
84
  */
85
- addSplit(name: string, split: ISplit): Promise<boolean> {
85
+ addSplit(split: ISplit): Promise<boolean> {
86
+ const name = split.name;
86
87
  const splitKey = this.keys.buildSplitKey(name);
87
88
  return this.redis.get(splitKey).then(splitFromStorage => {
88
89
 
@@ -107,18 +108,9 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
107
108
  }).then(() => true);
108
109
  }
109
110
 
110
- /**
111
- * Add a list of splits.
112
- * The returned promise is resolved when the operation success
113
- * or rejected if it fails (e.g., redis operation fails)
114
- */
115
- addSplits(entries: [string, ISplit][]): Promise<boolean[]> {
116
- return Promise.all(entries.map(keyValuePair => this.addSplit(keyValuePair[0], keyValuePair[1])));
117
- }
118
-
119
111
  /**
120
112
  * Remove a given split.
121
- * The returned promise is resolved when the operation success, with 1 or 0 indicating if the split existed or not.
113
+ * The returned promise is resolved when the operation success, with true or false indicating if the split existed (and was removed) or not.
122
114
  * or rejected if it fails (e.g., redis operation fails).
123
115
  */
124
116
  removeSplit(name: string) {
@@ -127,19 +119,10 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
127
119
  return this._decrementCounts(split).then(() => this._updateFlagSets(name, split.sets));
128
120
  }
129
121
  }).then(() => {
130
- return this.redis.del(this.keys.buildSplitKey(name));
122
+ return this.redis.del(this.keys.buildSplitKey(name)).then(status => status === 1);
131
123
  });
132
124
  }
133
125
 
134
- /**
135
- * Remove a list of splits.
136
- * The returned promise is resolved when the operation success,
137
- * or rejected if it fails (e.g., redis operation fails).
138
- */
139
- removeSplits(names: string[]): Promise<any> {
140
- return Promise.all(names.map(name => this.removeSplit(name)));
141
- }
142
-
143
126
  /**
144
127
  * Get split definition or null if it's not defined.
145
128
  * Returned promise is rejected if redis operation fails.
@@ -11,6 +11,7 @@ import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
11
11
  import { UniqueKeysCacheInRedis } from './UniqueKeysCacheInRedis';
12
12
  import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
13
13
  import { metadataBuilder } from '../utils';
14
+ import { RBSegmentsCacheInRedis } from './RBSegmentsCacheInRedis';
14
15
 
15
16
  export interface InRedisStorageOptions {
16
17
  prefix?: string
@@ -50,6 +51,7 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
50
51
 
51
52
  return {
52
53
  splits: new SplitsCacheInRedis(log, keys, redisClient, settings.sync.__splitFiltersValidation),
54
+ rbSegments: new RBSegmentsCacheInRedis(log, keys, redisClient),
53
55
  segments: new SegmentsCacheInRedis(log, keys, redisClient),
54
56
  impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
55
57
  impressionCounts: impressionCountsCache,
@@ -0,0 +1,76 @@
1
+ import { isNaNNumber } from '../../utils/lang';
2
+ import { KeyBuilder } from '../KeyBuilder';
3
+ import { IPluggableStorageWrapper, IRBSegmentsCacheAsync } from '../types';
4
+ import { ILogger } from '../../logger/types';
5
+ import { IRBSegment } from '../../dtos/types';
6
+ import { LOG_PREFIX } from './constants';
7
+ import { setToArray } from '../../utils/lang/sets';
8
+
9
+ export class RBSegmentsCachePluggable implements IRBSegmentsCacheAsync {
10
+
11
+ private readonly log: ILogger;
12
+ private readonly keys: KeyBuilder;
13
+ private readonly wrapper: IPluggableStorageWrapper;
14
+
15
+ constructor(log: ILogger, keys: KeyBuilder, wrapper: IPluggableStorageWrapper) {
16
+ this.log = log;
17
+ this.keys = keys;
18
+ this.wrapper = wrapper;
19
+ }
20
+
21
+ get(name: string): Promise<IRBSegment | null> {
22
+ return this.wrapper.get(this.keys.buildRBSegmentKey(name))
23
+ .then(maybeRBSegment => maybeRBSegment && JSON.parse(maybeRBSegment));
24
+ }
25
+
26
+ private getNames(): Promise<string[]> {
27
+ return this.wrapper.getKeysByPrefix(this.keys.buildRBSegmentKeyPrefix()).then(
28
+ (listOfKeys) => listOfKeys.map(this.keys.extractKey)
29
+ );
30
+ }
31
+
32
+ contains(names: Set<string>): Promise<boolean> {
33
+ const namesArray = setToArray(names);
34
+ return this.getNames().then(namesInStorage => {
35
+ return namesArray.every(name => namesInStorage.includes(name));
36
+ });
37
+ }
38
+
39
+ update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): Promise<boolean> {
40
+ return Promise.all([
41
+ this.setChangeNumber(changeNumber),
42
+ Promise.all(toAdd.map(toAdd => {
43
+ const key = this.keys.buildRBSegmentKey(toAdd.name);
44
+ const stringifiedNewRBSegment = JSON.stringify(toAdd);
45
+ return this.wrapper.set(key, stringifiedNewRBSegment).then(() => true);
46
+ })),
47
+ Promise.all(toRemove.map(toRemove => {
48
+ const key = this.keys.buildRBSegmentKey(toRemove.name);
49
+ return this.wrapper.del(key);
50
+ }))
51
+ ]).then(([, added, removed]) => {
52
+ return added.some(result => result) || removed.some(result => result);
53
+ });
54
+ }
55
+
56
+ setChangeNumber(changeNumber: number) {
57
+ return this.wrapper.set(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
58
+ }
59
+
60
+ getChangeNumber(): Promise<number> {
61
+ return this.wrapper.get(this.keys.buildRBSegmentsTillKey()).then((value) => {
62
+ const i = parseInt(value as string, 10);
63
+
64
+ return isNaNNumber(i) ? -1 : i;
65
+ }).catch((e) => {
66
+ this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from storage. Error: ' + e);
67
+ return -1;
68
+ });
69
+ }
70
+
71
+ // @TODO implement if required by DataLoader or producer mode
72
+ clear() {
73
+ return Promise.resolve();
74
+ }
75
+
76
+ }
@@ -66,7 +66,8 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
66
66
  * The returned promise is resolved when the operation success
67
67
  * or rejected if it fails (e.g., wrapper operation fails)
68
68
  */
69
- addSplit(name: string, split: ISplit): Promise<boolean> {
69
+ addSplit(split: ISplit): Promise<boolean> {
70
+ const name = split.name;
70
71
  const splitKey = this.keys.buildSplitKey(name);
71
72
  return this.wrapper.get(splitKey).then(splitFromStorage => {
72
73
 
@@ -91,15 +92,6 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
91
92
  }).then(() => true);
92
93
  }
93
94
 
94
- /**
95
- * Add a list of splits.
96
- * The returned promise is resolved when the operation success
97
- * or rejected if it fails (e.g., wrapper operation fails)
98
- */
99
- addSplits(entries: [string, ISplit][]): Promise<boolean[]> {
100
- return Promise.all(entries.map(keyValuePair => this.addSplit(keyValuePair[0], keyValuePair[1])));
101
- }
102
-
103
95
  /**
104
96
  * Remove a given split.
105
97
  * The returned promise is resolved when the operation success, with a boolean indicating if the split existed or not.
@@ -115,15 +107,6 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
115
107
  });
116
108
  }
117
109
 
118
- /**
119
- * Remove a list of splits.
120
- * The returned promise is resolved when the operation success, with a boolean array indicating if the splits existed or not.
121
- * or rejected if it fails (e.g., wrapper operation fails).
122
- */
123
- removeSplits(names: string[]): Promise<void> { // @ts-ignore
124
- return Promise.all(names.map(name => this.removeSplit(name)));
125
- }
126
-
127
110
  /**
128
111
  * Get split.
129
112
  * The returned promise is resolved with the split definition or null if it's not defined,
@@ -20,6 +20,7 @@ import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
20
20
  import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
21
21
  import { metadataBuilder } from '../utils';
22
22
  import { LOG_PREFIX } from '../pluggable/constants';
23
+ import { RBSegmentsCachePluggable } from './RBSegmentsCachePluggable';
23
24
 
24
25
  const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
25
26
  const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
@@ -116,6 +117,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
116
117
 
117
118
  return {
118
119
  splits: new SplitsCachePluggable(log, keys, wrapper, settings.sync.__splitFiltersValidation),
120
+ rbSegments: new RBSegmentsCachePluggable(log, keys, wrapper),
119
121
  segments: new SegmentsCachePluggable(log, keys, wrapper),
120
122
  impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
121
123
  impressionCounts: impressionCountsCache,