Skip to content

Commit b32e3ee

Browse files
Move validateCache logic outside SplitsCacheInLocal
1 parent e13f819 commit b32e3ee

File tree

5 files changed

+48
-51
lines changed

5 files changed

+48
-51
lines changed

src/storages/inLocalStorage/SplitsCacheInLocal.ts

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import { KeyBuilderCS } from '../KeyBuilderCS';
55
import { ILogger } from '../../logger/types';
66
import { LOG_PREFIX } from './constants';
77
import { ISettings } from '../../types';
8-
import { getStorageHash } from '../KeyBuilder';
98
import { setToArray } from '../../utils/lang/sets';
10-
import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
119

1210
/**
1311
* ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
@@ -26,42 +24,6 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
2624
this.flagSetsFilter = settings.sync.__splitFiltersValidation.groupedFilters.bySet;
2725
}
2826

29-
/**
30-
* Clean Splits cache if:
31-
* - it has expired, i.e., its `lastUpdated` timestamp is older than the given `expirationTimestamp`
32-
* - hash has changes, i.e., the SDK key, flags filter criteria or flags spec version was modified
33-
*/
34-
public validateCache(settings: ISettings) {
35-
// _checkExpiration
36-
const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
37-
let value: string | number | null = localStorage.getItem(this.keys.buildLastUpdatedKey());
38-
if (value !== null) {
39-
value = parseInt(value, 10);
40-
if (!isNaNNumber(value) && value < expirationTimestamp) this.clear();
41-
}
42-
43-
// @TODO eventually remove `_checkFilterQuery`. Cache should be cleared at the storage level, reusing same logic than PluggableStorage
44-
// _checkFilterQuery
45-
const storageHashKey = this.keys.buildHashKey();
46-
const storageHash = localStorage.getItem(storageHashKey);
47-
const currentStorageHash = getStorageHash(settings);
48-
49-
if (storageHash !== currentStorageHash) {
50-
this.log.info(LOG_PREFIX + 'SDK key, flags filter criteria or flags spec version was modified. Updating cache');
51-
try {
52-
// if there is cache, clear it
53-
if (this.getChangeNumber() > -1) this.clear();
54-
55-
localStorage.setItem(storageHashKey, currentStorageHash);
56-
} catch (e) {
57-
this.log.error(LOG_PREFIX + e);
58-
}
59-
}
60-
// if the filter didn't change, nothing is done
61-
62-
return this.getChangeNumber() > -1;
63-
}
64-
6527
private _decrementCount(key: string) {
6628
const count = toNumber(localStorage.getItem(key)) - 1;
6729
// @ts-expect-error

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { fullSettings } from '../../../utils/settingsValidation/__tests__/settin
77

88
test('SPLIT CACHE / LocalStorage', () => {
99
const cache = new SplitsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
10-
cache.validateCache(fullSettings);
1110

1211
cache.clear();
1312

@@ -41,7 +40,6 @@ test('SPLIT CACHE / LocalStorage', () => {
4140

4241
test('SPLIT CACHE / LocalStorage / Get Keys', () => {
4342
const cache = new SplitsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
44-
cache.validateCache(fullSettings);
4543

4644
cache.addSplit('lol1', something);
4745
cache.addSplit('lol2', somethingElse);
@@ -54,7 +52,6 @@ test('SPLIT CACHE / LocalStorage / Get Keys', () => {
5452

5553
test('SPLIT CACHE / LocalStorage / Add Splits', () => {
5654
const cache = new SplitsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
57-
cache.validateCache(fullSettings);
5855

5956
cache.addSplits([
6057
['lol1', something],
@@ -69,7 +66,6 @@ test('SPLIT CACHE / LocalStorage / Add Splits', () => {
6966

7067
test('SPLIT CACHE / LocalStorage / trafficTypeExists and ttcache tests', () => {
7168
const cache = new SplitsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
72-
cache.validateCache(fullSettings);
7369

7470
cache.addSplits([ // loop of addSplit
7571
['split1', splitWithUserTT],
@@ -108,7 +104,6 @@ test('SPLIT CACHE / LocalStorage / trafficTypeExists and ttcache tests', () => {
108104

109105
test('SPLIT CACHE / LocalStorage / killLocally', () => {
110106
const cache = new SplitsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
111-
cache.validateCache(fullSettings);
112107

113108
cache.addSplit('lol1', something);
114109
cache.addSplit('lol2', somethingElse);
@@ -142,7 +137,6 @@ test('SPLIT CACHE / LocalStorage / killLocally', () => {
142137

143138
test('SPLIT CACHE / LocalStorage / usesSegments', () => {
144139
const cache = new SplitsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
145-
cache.validateCache(fullSettings);
146140

147141
expect(cache.usesSegments()).toBe(true); // true initially, until data is synchronized
148142
cache.setChangeNumber(1); // to indicate that data has been synced.
@@ -174,7 +168,6 @@ test('SPLIT CACHE / LocalStorage / flag set cache tests', () => {
174168
}
175169
}
176170
}, new KeyBuilderCS('SPLITIO', 'user'));
177-
cache.validateCache(fullSettings);
178171

179172
const emptySet = new Set([]);
180173

@@ -216,7 +209,6 @@ test('SPLIT CACHE / LocalStorage / flag set cache tests', () => {
216209
// if FlagSets are not defined, it should store all FlagSets in memory.
217210
test('SPLIT CACHE / LocalStorage / flag set cache tests without filters', () => {
218211
const cache = new SplitsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
219-
cache.validateCache(fullSettings);
220212

221213
const emptySet = new Set([]);
222214

@@ -236,6 +228,5 @@ test('SPLIT CACHE / LocalStorage / flag set cache tests without filters', () =>
236228

237229
// Validate that the feature flag cache is cleared when calling `clear` method
238230
cache.clear();
239-
expect(localStorage.length).toBe(1); // only 'SPLITIO.hash' should remain in localStorage
240-
expect(localStorage.key(0)).toBe('SPLITIO.hash');
231+
expect(localStorage.length).toBe(0);
241232
});

src/storages/inLocalStorage/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { DEBUG, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
1313
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
1414
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
1515
import { getMatching } from '../../utils/key';
16+
import { validateCache } from './validateCache';
1617

1718
export interface InLocalStorageOptions {
1819
prefix?: string
@@ -51,9 +52,8 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
5152
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
5253
uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
5354

54-
// @TODO implement
5555
validateCache() {
56-
return splits.validateCache(settings);
56+
return validateCache(settings, keys, splits);
5757
},
5858

5959
destroy() { },
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { ISettings } from '../../types';
2+
import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
3+
import { isNaNNumber } from '../../utils/lang';
4+
import { getStorageHash } from '../KeyBuilder';
5+
import { LOG_PREFIX } from './constants';
6+
import type { SplitsCacheInLocal } from './SplitsCacheInLocal';
7+
import { KeyBuilderCS } from '../KeyBuilderCS';
8+
9+
/**
10+
* Clean cache if:
11+
* - it has expired, i.e., its `lastUpdated` timestamp is older than the given `expirationTimestamp`
12+
* - hash has changed, i.e., the SDK key, flags filter criteria or flags spec version was modified
13+
*/
14+
export function validateCache(settings: ISettings, keys: KeyBuilderCS, splits: SplitsCacheInLocal): boolean {
15+
const { log } = settings;
16+
17+
// Check expiration and clear cache if needed
18+
const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
19+
let value: string | number | null = localStorage.getItem(keys.buildLastUpdatedKey());
20+
if (value !== null) {
21+
value = parseInt(value, 10);
22+
if (!isNaNNumber(value) && value < expirationTimestamp) splits.clear();
23+
}
24+
25+
// Check hash and clear cache if needed
26+
const storageHashKey = keys.buildHashKey();
27+
const storageHash = localStorage.getItem(storageHashKey);
28+
const currentStorageHash = getStorageHash(settings);
29+
30+
if (storageHash !== currentStorageHash) {
31+
log.info(LOG_PREFIX + 'SDK key, flags filter criteria or flags spec version was modified. Updating cache');
32+
try {
33+
if (splits.getChangeNumber() > -1) splits.clear();
34+
35+
localStorage.setItem(storageHashKey, currentStorageHash);
36+
} catch (e) {
37+
log.error(LOG_PREFIX + e);
38+
}
39+
}
40+
41+
// Check if the cache is ready
42+
return splits.getChangeNumber() > -1;
43+
}

src/storages/pluggable/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
9292
// Connects to wrapper and emits SDK_READY event on main client
9393
const connectPromise = wrapper.connect().then(() => {
9494
if (isSyncronizer) {
95-
// In standalone or producer mode, clear storage if SDK key or feature flag filter has changed
95+
// @TODO reuse InLocalStorage::validateCache logic
96+
// In standalone or producer mode, clear storage if SDK key, flags filter criteria or flags spec version was modified
9697
return wrapper.get(keys.buildHashKey()).then((hash) => {
9798
const currentHash = getStorageHash(settings);
9899
if (hash !== currentHash) {

0 commit comments

Comments
 (0)