@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.
- package/README.md +1 -0
- package/cjs/evaluator/combiners/and.js +2 -6
- package/cjs/evaluator/combiners/ifelseif.js +6 -6
- package/cjs/evaluator/condition/index.js +6 -5
- package/cjs/evaluator/index.js +7 -7
- package/cjs/evaluator/matchers/index.js +3 -1
- package/cjs/evaluator/matchers/matcherTypes.js +1 -0
- package/cjs/evaluator/matchers/rbsegment.js +43 -0
- package/cjs/evaluator/matchersTransform/index.js +4 -0
- package/cjs/evaluator/parser/index.js +2 -2
- package/cjs/evaluator/value/sanitize.js +1 -0
- package/cjs/logger/constants.js +5 -6
- package/cjs/logger/messages/debug.js +3 -4
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/services/splitApi.js +2 -2
- package/cjs/storages/AbstractSplitsCacheAsync.js +12 -1
- package/cjs/storages/AbstractSplitsCacheSync.js +10 -9
- package/cjs/storages/KeyBuilder.js +8 -15
- package/cjs/storages/KeyBuilderCS.js +12 -3
- package/cjs/storages/KeyBuilderSS.js +3 -0
- package/cjs/storages/dataLoader.js +1 -2
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +117 -0
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +14 -16
- package/cjs/storages/inLocalStorage/index.js +4 -0
- package/cjs/storages/inMemory/InMemoryStorage.js +3 -0
- package/cjs/storages/inMemory/InMemoryStorageCS.js +4 -0
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +61 -0
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +24 -31
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +64 -0
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +4 -21
- package/cjs/storages/inRedis/index.js +2 -0
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +64 -0
- package/cjs/storages/pluggable/SplitsCachePluggable.js +2 -19
- package/cjs/storages/pluggable/index.js +2 -0
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +12 -13
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +2 -2
- package/cjs/sync/polling/pollingManagerCS.js +7 -7
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +53 -51
- package/cjs/sync/streaming/SSEHandler/index.js +1 -0
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +106 -77
- package/cjs/sync/streaming/constants.js +2 -1
- package/cjs/sync/streaming/pushManager.js +3 -16
- package/cjs/sync/syncManagerOnline.js +2 -2
- package/cjs/utils/constants/index.js +3 -2
- package/esm/evaluator/combiners/and.js +2 -6
- package/esm/evaluator/combiners/ifelseif.js +7 -7
- package/esm/evaluator/condition/index.js +6 -5
- package/esm/evaluator/index.js +7 -7
- package/esm/evaluator/matchers/index.js +3 -1
- package/esm/evaluator/matchers/matcherTypes.js +1 -0
- package/esm/evaluator/matchers/rbsegment.js +39 -0
- package/esm/evaluator/matchersTransform/index.js +4 -0
- package/esm/evaluator/parser/index.js +2 -2
- package/esm/evaluator/value/sanitize.js +1 -0
- package/esm/logger/constants.js +2 -3
- package/esm/logger/messages/debug.js +3 -4
- package/esm/logger/messages/warn.js +1 -1
- package/esm/services/splitApi.js +2 -2
- package/esm/storages/AbstractSplitsCacheAsync.js +12 -1
- package/esm/storages/AbstractSplitsCacheSync.js +10 -9
- package/esm/storages/KeyBuilder.js +8 -15
- package/esm/storages/KeyBuilderCS.js +12 -3
- package/esm/storages/KeyBuilderSS.js +3 -0
- package/esm/storages/dataLoader.js +1 -2
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +114 -0
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +14 -16
- package/esm/storages/inLocalStorage/index.js +4 -0
- package/esm/storages/inMemory/InMemoryStorage.js +3 -0
- package/esm/storages/inMemory/InMemoryStorageCS.js +4 -0
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +58 -0
- package/esm/storages/inMemory/SplitsCacheInMemory.js +24 -31
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +61 -0
- package/esm/storages/inRedis/SplitsCacheInRedis.js +4 -21
- package/esm/storages/inRedis/index.js +2 -0
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +61 -0
- package/esm/storages/pluggable/SplitsCachePluggable.js +2 -19
- package/esm/storages/pluggable/index.js +2 -0
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +12 -13
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +2 -2
- package/esm/sync/polling/pollingManagerCS.js +7 -7
- package/esm/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/esm/sync/polling/updaters/splitChangesUpdater.js +53 -51
- package/esm/sync/streaming/SSEHandler/index.js +2 -1
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +102 -73
- package/esm/sync/streaming/constants.js +1 -0
- package/esm/sync/streaming/pushManager.js +6 -19
- package/esm/sync/syncManagerOnline.js +2 -2
- package/esm/utils/constants/index.js +2 -1
- package/package.json +1 -1
- package/src/dtos/types.ts +32 -8
- package/src/evaluator/Engine.ts +1 -1
- package/src/evaluator/combiners/and.ts +5 -4
- package/src/evaluator/combiners/ifelseif.ts +7 -9
- package/src/evaluator/condition/engineUtils.ts +1 -1
- package/src/evaluator/condition/index.ts +12 -12
- package/src/evaluator/index.ts +7 -7
- package/src/evaluator/matchers/index.ts +3 -1
- package/src/evaluator/matchers/matcherTypes.ts +1 -0
- package/src/evaluator/matchers/rbsegment.ts +61 -0
- package/src/evaluator/matchersTransform/index.ts +3 -0
- package/src/evaluator/parser/index.ts +3 -3
- package/src/evaluator/types.ts +2 -2
- package/src/evaluator/value/index.ts +2 -2
- package/src/evaluator/value/sanitize.ts +5 -4
- package/src/logger/constants.ts +2 -3
- package/src/logger/messages/debug.ts +3 -4
- package/src/logger/messages/warn.ts +1 -1
- package/src/sdkManager/index.ts +1 -1
- package/src/services/splitApi.ts +2 -2
- package/src/services/types.ts +1 -1
- package/src/storages/AbstractSplitsCacheAsync.ts +15 -5
- package/src/storages/AbstractSplitsCacheSync.ts +14 -15
- package/src/storages/KeyBuilder.ts +9 -17
- package/src/storages/KeyBuilderCS.ts +15 -4
- package/src/storages/KeyBuilderSS.ts +4 -0
- package/src/storages/dataLoader.ts +1 -2
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +136 -0
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +15 -16
- package/src/storages/inLocalStorage/index.ts +4 -0
- package/src/storages/inMemory/InMemoryStorage.ts +3 -0
- package/src/storages/inMemory/InMemoryStorageCS.ts +4 -0
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +68 -0
- package/src/storages/inMemory/SplitsCacheInMemory.ts +22 -27
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +79 -0
- package/src/storages/inRedis/SplitsCacheInRedis.ts +4 -21
- package/src/storages/inRedis/index.ts +2 -0
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +76 -0
- package/src/storages/pluggable/SplitsCachePluggable.ts +2 -19
- package/src/storages/pluggable/index.ts +2 -0
- package/src/storages/types.ts +43 -17
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +14 -15
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +2 -1
- package/src/sync/polling/fetchers/types.ts +1 -0
- package/src/sync/polling/pollingManagerCS.ts +7 -7
- package/src/sync/polling/syncTasks/splitsSyncTask.ts +1 -2
- package/src/sync/polling/types.ts +2 -2
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -2
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
- package/src/sync/polling/updaters/splitChangesUpdater.ts +64 -62
- package/src/sync/streaming/SSEHandler/index.ts +2 -1
- package/src/sync/streaming/SSEHandler/types.ts +2 -2
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +98 -68
- package/src/sync/streaming/constants.ts +1 -0
- package/src/sync/streaming/parseUtils.ts +2 -2
- package/src/sync/streaming/pushManager.ts +6 -18
- package/src/sync/streaming/types.ts +3 -2
- package/src/sync/syncManagerOnline.ts +2 -2
- package/src/utils/constants/index.ts +2 -1
- 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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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(
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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(
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
51
|
+
// Add to segments count for the new version of the Split
|
|
52
|
+
if (usesSegments(split)) this.segmentsCount++;
|
|
53
53
|
|
|
54
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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(
|
|
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
|
|
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(
|
|
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,
|