Skip to content

Commit 84d133e

Browse files
Move usesSegments into an utility function for reusability
1 parent 8bf9566 commit 84d133e

13 files changed

+41
-118
lines changed

src/storages/AbstractSplitsCacheAsync.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,6 @@ export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
3131
abstract trafficTypeExists(trafficType: string): Promise<boolean>
3232
abstract clear(): Promise<boolean | void>
3333

34-
// @TODO revisit segment-related methods ('usesSegments')
35-
// noop, just keeping the interface. This is used by standalone client-side API only, and so only implemented by InMemory and InLocalStorage.
36-
usesSegments(): Promise<boolean> {
37-
return Promise.resolve(true);
38-
}
39-
4034
/**
4135
* Kill `name` split and set `defaultTreatment` and `changeNumber`.
4236
* Used for SPLIT_KILL push notifications.

src/storages/AbstractSplitsCacheSync.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ISplitsCacheSync } from './types';
1+
import { ISplitsCacheSync, IStorageSync } from './types';
22
import { IRBSegment, ISplit } from '../dtos/types';
33
import { objectAssign } from '../utils/lang/objectAssign';
44
import { IN_SEGMENT, IN_LARGE_SEGMENT } from '../utils/constants';
@@ -39,8 +39,6 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
3939

4040
abstract trafficTypeExists(trafficType: string): boolean
4141

42-
abstract usesSegments(): boolean
43-
4442
abstract clear(): void
4543

4644
/**
@@ -72,7 +70,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
7270
* Given a parsed split, it returns a boolean flagging if its conditions use segments matchers (rules & whitelists).
7371
* This util is intended to simplify the implementation of `splitsCache::usesSegments` method
7472
*/
75-
export function usesSegments(ruleEntity: ISplit | IRBSegment) {
73+
function hasSegments(ruleEntity: ISplit | IRBSegment) {
7674
const conditions = ruleEntity.conditions || [];
7775
for (let i = 0; i < conditions.length; i++) {
7876
const matchers = conditions[i].matcherGroup.matchers;
@@ -88,3 +86,9 @@ export function usesSegments(ruleEntity: ISplit | IRBSegment) {
8886

8987
return false;
9088
}
89+
90+
export function usesSegments(storage: Pick<IStorageSync, 'splits' | 'rbSegments'>) {
91+
return storage.splits.getChangeNumber() === -1 ||
92+
storage.splits.getAll().some(split => hasSegments(split)) ||
93+
storage.rbSegments.getAll().some(rbSegment => hasSegments(rbSegment));
94+
}

src/storages/KeyBuilderCS.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@ export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
5151
return startsWith(key, `${this.prefix}.rbsegment.`);
5252
}
5353

54-
buildSplitsWithSegmentCountKey() {
55-
return `${this.prefix}.splits.usingSegments`;
56-
}
57-
5854
buildLastClear() {
5955
return `${this.prefix}.lastClear`;
6056
}

src/storages/__tests__/RBSegmentsCacheSync.spec.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { KeyBuilderCS } from '../KeyBuilderCS';
44
import { rbSegment, rbSegmentWithInSegmentMatcher } from '../__tests__/testUtils';
55
import { IRBSegmentsCacheSync } from '../types';
66
import { fullSettings } from '../../utils/settingsValidation/__tests__/settings.mocks';
7+
import { usesSegments } from '../AbstractSplitsCacheSync';
8+
import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
79

810
const cacheInMemory = new RBSegmentsCacheInMemory();
911
const cacheInLocal = new RBSegmentsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
@@ -61,15 +63,18 @@ describe.each([cacheInMemory, cacheInLocal])('Rule-based segments cache sync (Me
6163
});
6264

6365
test('usesSegments should track segments usage correctly', () => {
64-
expect(cache.usesSegments()).toBe(false); // No rbSegments, so false
66+
const storage = { rbSegments: cache, splits: new SplitsCacheInMemory() };
67+
storage.splits.setChangeNumber(1);
68+
69+
expect(usesSegments(storage)).toBe(false); // No rbSegments, so false
6570

6671
cache.update([rbSegment], [], 1); // rbSegment doesn't have IN_SEGMENT matcher
67-
expect(cache.usesSegments()).toBe(false);
72+
expect(usesSegments(storage)).toBe(false);
6873

6974
cache.update([rbSegmentWithInSegmentMatcher], [], 2); // rbSegmentWithInSegmentMatcher has IN_SEGMENT matcher
70-
expect(cache.usesSegments()).toBe(true);
75+
expect(usesSegments(storage)).toBe(true);
7176

7277
cache.clear();
73-
expect(cache.usesSegments()).toBe(false); // False after clear since there are no rbSegments
78+
expect(usesSegments(storage)).toBe(false); // False after clear since there are no rbSegments
7479
});
7580
});

src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { IRBSegment } from '../../dtos/types';
22
import { ILogger } from '../../logger/types';
33
import { ISettings } from '../../types';
4-
import { isFiniteNumber, isNaNNumber, toNumber } from '../../utils/lang';
4+
import { isNaNNumber } from '../../utils/lang';
55
import { setToArray } from '../../utils/lang/sets';
6-
import { usesSegments } from '../AbstractSplitsCacheSync';
76
import { KeyBuilderCS } from '../KeyBuilderCS';
87
import { IRBSegmentsCacheSync } from '../types';
98
import { LOG_PREFIX } from './constants';
@@ -38,28 +37,13 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
3837
}
3938
}
4039

41-
private updateSegmentCount(diff: number) {
42-
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
43-
const count = toNumber(localStorage.getItem(segmentsCountKey)) + diff;
44-
// @ts-expect-error
45-
if (count > 0) localStorage.setItem(segmentsCountKey, count);
46-
else localStorage.removeItem(segmentsCountKey);
47-
}
48-
4940
private add(rbSegment: IRBSegment): boolean {
5041
try {
5142
const name = rbSegment.name;
5243
const rbSegmentKey = this.keys.buildRBSegmentKey(name);
53-
const rbSegmentFromLocalStorage = localStorage.getItem(rbSegmentKey);
54-
const previous = rbSegmentFromLocalStorage ? JSON.parse(rbSegmentFromLocalStorage) : null;
5544

5645
localStorage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
5746

58-
let usesSegmentsDiff = 0;
59-
if (previous && usesSegments(previous)) usesSegmentsDiff--;
60-
if (usesSegments(rbSegment)) usesSegmentsDiff++;
61-
if (usesSegmentsDiff !== 0) this.updateSegmentCount(usesSegmentsDiff);
62-
6347
return true;
6448
} catch (e) {
6549
this.log.error(LOG_PREFIX + e);
@@ -74,8 +58,6 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
7458

7559
localStorage.removeItem(this.keys.buildRBSegmentKey(name));
7660

77-
if (usesSegments(rbSegment)) this.updateSegmentCount(-1);
78-
7961
return true;
8062
} catch (e) {
8163
this.log.error(LOG_PREFIX + e);
@@ -128,13 +110,4 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
128110
return n;
129111
}
130112

131-
usesSegments(): boolean {
132-
const storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
133-
const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
134-
135-
return isFiniteNumber(splitsWithSegmentsCount) ?
136-
splitsWithSegmentsCount > 0 :
137-
true;
138-
}
139-
140113
}

src/storages/inLocalStorage/SplitsCacheInLocal.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ISplit } from '../../dtos/types';
2-
import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
2+
import { AbstractSplitsCacheSync } from '../AbstractSplitsCacheSync';
33
import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
44
import { KeyBuilderCS } from '../KeyBuilderCS';
55
import { ILogger } from '../../logger/types';
@@ -35,11 +35,6 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
3535
try {
3636
const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
3737
this._decrementCount(ttKey);
38-
39-
if (usesSegments(split)) {
40-
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
41-
this._decrementCount(segmentsCountKey);
42-
}
4338
} catch (e) {
4439
this.log.error(LOG_PREFIX + e);
4540
}
@@ -50,12 +45,6 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
5045
const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
5146
// @ts-expect-error
5247
localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
53-
54-
if (usesSegments(split)) {
55-
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
56-
// @ts-expect-error
57-
localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
58-
}
5948
} catch (e) {
6049
this.log.error(LOG_PREFIX + e);
6150
}
@@ -176,18 +165,6 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
176165
return isFiniteNumber(ttCount) && ttCount > 0;
177166
}
178167

179-
usesSegments() {
180-
// If cache hasn't been synchronized with the cloud, assume we need them.
181-
if (!this.hasSync) return true;
182-
183-
const storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
184-
const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
185-
186-
return isFiniteNumber(splitsWithSegmentsCount) ?
187-
splitsWithSegmentsCount > 0 :
188-
true;
189-
}
190-
191168
getNamesByFlagSets(flagSets: string[]): Set<string>[] {
192169
return flagSets.map(flagSet => {
193170
const flagSetKey = this.keys.buildFlagSetKey(flagSet);

src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { KeyBuilderCS } from '../../KeyBuilderCS';
33
import { splitWithUserTT, splitWithAccountTT, splitWithAccountTTAndUsesSegments, something, somethingElse, featureFlagOne, featureFlagTwo, featureFlagThree, featureFlagWithEmptyFS, featureFlagWithoutFS } from '../../__tests__/testUtils';
44
import { ISplit } from '../../../dtos/types';
55
import { fullSettings } from '../../../utils/settingsValidation/__tests__/settings.mocks';
6+
import { RBSegmentsCacheInMemory } from '../../inMemory/RBSegmentsCacheInMemory';
7+
import { usesSegments } from '../../AbstractSplitsCacheSync';
68

79

810
test('SPLITS CACHE / LocalStorage', () => {
@@ -132,24 +134,26 @@ test('SPLITS CACHE / LocalStorage / killLocally', () => {
132134

133135
test('SPLITS CACHE / LocalStorage / usesSegments', () => {
134136
const cache = new SplitsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
137+
cache.clear();
138+
const storage = { splits: cache, rbSegments: new RBSegmentsCacheInMemory() };
135139

136-
expect(cache.usesSegments()).toBe(true); // true initially, until data is synchronized
140+
expect(usesSegments(storage)).toBe(true); // true initially, until data is synchronized
137141
cache.setChangeNumber(1); // to indicate that data has been synced.
138142

139143
cache.update([splitWithUserTT, splitWithAccountTT], [], 1);
140-
expect(cache.usesSegments()).toBe(false); // 0 splits using segments
144+
expect(usesSegments(storage)).toBe(false); // 0 splits using segments
141145

142146
cache.addSplit({ ...splitWithAccountTTAndUsesSegments, name: 'split3' });
143-
expect(cache.usesSegments()).toBe(true); // 1 split using segments
147+
expect(usesSegments(storage)).toBe(true); // 1 split using segments
144148

145149
cache.addSplit({ ...splitWithAccountTTAndUsesSegments, name: 'split4' });
146-
expect(cache.usesSegments()).toBe(true); // 2 splits using segments
150+
expect(usesSegments(storage)).toBe(true); // 2 splits using segments
147151

148152
cache.removeSplit('split3');
149-
expect(cache.usesSegments()).toBe(true); // 1 split using segments
153+
expect(usesSegments(storage)).toBe(true); // 1 split using segments
150154

151155
cache.removeSplit('split4');
152-
expect(cache.usesSegments()).toBe(false); // 0 splits using segments
156+
expect(usesSegments(storage)).toBe(false); // 0 splits using segments
153157
});
154158

155159
test('SPLITS CACHE / LocalStorage / flag set cache tests', () => {

src/storages/inMemory/RBSegmentsCacheInMemory.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import { IRBSegment } from '../../dtos/types';
22
import { setToArray } from '../../utils/lang/sets';
3-
import { usesSegments } from '../AbstractSplitsCacheSync';
43
import { IRBSegmentsCacheSync } from '../types';
54

65
export class RBSegmentsCacheInMemory implements IRBSegmentsCacheSync {
76

87
private cache: Record<string, IRBSegment> = {};
98
private changeNumber: number = -1;
10-
private segmentsCount: number = 0;
119

1210
clear() {
1311
this.cache = {};
1412
this.changeNumber = -1;
15-
this.segmentsCount = 0;
1613
}
1714

1815
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
@@ -23,11 +20,8 @@ export class RBSegmentsCacheInMemory implements IRBSegmentsCacheSync {
2320

2421
private add(rbSegment: IRBSegment): boolean {
2522
const name = rbSegment.name;
26-
const previous = this.get(name);
27-
if (previous && usesSegments(previous)) this.segmentsCount--;
2823

2924
this.cache[name] = rbSegment;
30-
if (usesSegments(rbSegment)) this.segmentsCount++;
3125

3226
return true;
3327
}
@@ -38,8 +32,6 @@ export class RBSegmentsCacheInMemory implements IRBSegmentsCacheSync {
3832

3933
delete this.cache[name];
4034

41-
if (usesSegments(rbSegment)) this.segmentsCount--;
42-
4335
return true;
4436
}
4537

@@ -65,8 +57,4 @@ export class RBSegmentsCacheInMemory implements IRBSegmentsCacheSync {
6557
return this.changeNumber;
6658
}
6759

68-
usesSegments(): boolean {
69-
return this.segmentsCount > 0;
70-
}
71-
7260
}

src/storages/inMemory/SplitsCacheInMemory.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
2-
import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
2+
import { AbstractSplitsCacheSync } from '../AbstractSplitsCacheSync';
33
import { isFiniteNumber } from '../../utils/lang';
44

55
/**
@@ -11,7 +11,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
1111
private splitsCache: Record<string, ISplit> = {};
1212
private ttCache: Record<string, number> = {};
1313
private changeNumber: number = -1;
14-
private segmentsCount: number = 0;
1514
private flagSetsCache: Record<string, Set<string>> = {};
1615

1716
constructor(splitFiltersValidation?: ISplitFiltersValidation) {
@@ -23,7 +22,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
2322
this.splitsCache = {};
2423
this.ttCache = {};
2524
this.changeNumber = -1;
26-
this.segmentsCount = 0;
2725
this.flagSetsCache = {};
2826
}
2927

@@ -37,9 +35,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
3735
if (!this.ttCache[previousTtName]) delete this.ttCache[previousTtName];
3836

3937
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
40-
41-
// Subtract from segments count for the previous version of this Split
42-
if (usesSegments(previousSplit)) this.segmentsCount--;
4338
}
4439

4540
// Store the Split.
@@ -49,9 +44,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
4944
this.ttCache[ttName] = (this.ttCache[ttName] || 0) + 1;
5045
this.addToFlagSets(split);
5146

52-
// Add to segments count for the new version of the Split
53-
if (usesSegments(split)) this.segmentsCount++;
54-
5547
return true;
5648
}
5749

@@ -67,9 +59,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
6759
if (!this.ttCache[ttName]) delete this.ttCache[ttName];
6860
this.removeFromFlagSets(split.name, split.sets);
6961

70-
// Update the segments count.
71-
if (usesSegments(split)) this.segmentsCount--;
72-
7362
return true;
7463
}
7564

@@ -94,10 +83,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
9483
return isFiniteNumber(this.ttCache[trafficType]) && this.ttCache[trafficType] > 0;
9584
}
9685

97-
usesSegments(): boolean {
98-
return this.getChangeNumber() === -1 || this.segmentsCount > 0;
99-
}
100-
10186
getNamesByFlagSets(flagSets: string[]): Set<string>[] {
10287
return flagSets.map(flagSet => this.flagSetsCache[flagSet] || new Set());
10388
}

src/storages/types.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,6 @@ export interface ISplitsCacheBase {
186186
getSplitNames(): MaybeThenable<string[]>,
187187
// should never reject or throw an exception. Instead return true by default, asssuming the TT might exist.
188188
trafficTypeExists(trafficType: string): MaybeThenable<boolean>,
189-
// only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === -1) or contains a FF using segments or large segments
190-
usesSegments(): MaybeThenable<boolean>,
191189
clear(): MaybeThenable<boolean | void>,
192190
killLocally(name: string, defaultTreatment: string, changeNumber: number): MaybeThenable<boolean>,
193191
getNamesByFlagSets(flagSets: string[]): MaybeThenable<Set<string>[]>
@@ -201,7 +199,6 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
201199
getAll(): ISplit[],
202200
getSplitNames(): string[],
203201
trafficTypeExists(trafficType: string): boolean,
204-
usesSegments(): boolean,
205202
clear(): void,
206203
killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
207204
getNamesByFlagSets(flagSets: string[]): Set<string>[]
@@ -215,7 +212,6 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
215212
getAll(): Promise<ISplit[]>,
216213
getSplitNames(): Promise<string[]>,
217214
trafficTypeExists(trafficType: string): Promise<boolean>,
218-
usesSegments(): Promise<boolean>,
219215
clear(): Promise<boolean | void>,
220216
killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
221217
getNamesByFlagSets(flagSets: string[]): Promise<Set<string>[]>
@@ -239,8 +235,6 @@ export interface IRBSegmentsCacheSync extends IRBSegmentsCacheBase {
239235
clear(): void,
240236
contains(names: Set<string>): boolean,
241237
getAll(): IRBSegment[],
242-
// Used only for smart pausing in client-side standalone. Returns true if the storage contains a RBSegment using segments or large segments matchers
243-
usesSegments(): boolean,
244238
}
245239

246240
export interface IRBSegmentsCacheAsync extends IRBSegmentsCacheBase {

0 commit comments

Comments
 (0)