Skip to content

Commit 38092dc

Browse files
Add onReadyFromCacheCb to storage factory params for code cleanup
1 parent d2fbd1b commit 38092dc

File tree

11 files changed

+19
-76
lines changed

11 files changed

+19
-76
lines changed

src/sdkFactory/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { IBasicClient, SplitIO } from '../types';
77
import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
88
import { createLoggerAPI } from '../logger/sdkLogger';
99
import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
10-
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
10+
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
1111
import { objectAssign } from '../utils/lang/objectAssign';
1212
import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
1313
import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
@@ -46,6 +46,9 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
4646
readiness.splits.emit(SDK_SPLITS_ARRIVED);
4747
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
4848
},
49+
onReadyFromCacheCb: () => {
50+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
51+
}
4952
});
5053
// @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
5154
const clients: Record<string, IBasicClient> = {};

src/storages/AbstractSplitsCacheAsync.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,6 @@ export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
2828
return Promise.resolve(true);
2929
}
3030

31-
/**
32-
* Check if the splits information is already stored in cache.
33-
* Noop, just keeping the interface. This is used by client-side implementations only.
34-
*/
35-
checkCache(): Promise<boolean> {
36-
return Promise.resolve(false);
37-
}
38-
3931
/**
4032
* Kill `name` split and set `defaultTreatment` and `changeNumber`.
4133
* Used for SPLIT_KILL push notifications.

src/storages/AbstractSplitsCacheSync.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,6 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
4848

4949
abstract clear(): void
5050

51-
/**
52-
* Check if the splits information is already stored in cache. This data can be preloaded.
53-
* It is used as condition to emit SDK_SPLITS_CACHE_LOADED, and then SDK_READY_FROM_CACHE.
54-
*/
55-
checkCache(): boolean {
56-
return false;
57-
}
58-
5951
/**
6052
* Kill `name` split and set `defaultTreatment` and `changeNumber`.
6153
* Used for SPLIT_KILL push notifications.

src/storages/inLocalStorage/SplitsCacheInLocal.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,6 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
217217
}
218218
}
219219

220-
/**
221-
* Check if the splits information is already stored in browser LocalStorage.
222-
* In this function we could add more code to check if the data is valid.
223-
* @override
224-
*/
225-
checkCache(): boolean {
226-
return this.getChangeNumber() > -1;
227-
}
228-
229220
/**
230221
* Clean Splits cache if its `lastUpdated` timestamp is older than the given `expirationTimestamp`,
231222
*
@@ -250,7 +241,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
250241
this.updateNewFilter = true;
251242

252243
// if there is cache, clear it
253-
if (this.checkCache()) this.clear();
244+
if (this.getChangeNumber() > -1) this.clear();
254245

255246
} catch (e) {
256247
this.log.error(LOG_PREFIX + e);

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,10 @@ test('SPLIT CACHE / LocalStorage', () => {
3131
expect(cache.getSplit('lol1')).toEqual(null);
3232
expect(cache.getSplit('lol2')).toEqual(somethingElse);
3333

34-
expect(cache.checkCache()).toBe(false); // checkCache should return false until localstorage has data.
35-
3634
expect(cache.getChangeNumber() === -1).toBe(true);
3735

38-
expect(cache.checkCache()).toBe(false); // checkCache should return false until localstorage has data.
39-
4036
cache.setChangeNumber(123);
4137

42-
expect(cache.checkCache()).toBe(true); // checkCache should return true once localstorage has data.
43-
4438
expect(cache.getChangeNumber() === 123).toBe(true);
4539

4640
});

src/storages/inLocalStorage/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
1212
import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
1313
import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
1414
import { LOG_PREFIX } from './constants';
15-
import { DEBUG, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
15+
import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
1616
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
1717
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
1818
import { getMatching } from '../../utils/key';
@@ -36,7 +36,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
3636
return InMemoryStorageCSFactory(params);
3737
}
3838

39-
const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
39+
const { onReadyFromCacheCb, settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
4040
const matchingKey = getMatching(settings.core.key);
4141
const keys = new KeyBuilderCS(prefix, matchingKey);
4242
const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
@@ -45,6 +45,10 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
4545
const segments = new MySegmentsCacheInLocal(log, keys);
4646
const largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey));
4747

48+
if (settings.mode === LOCALHOST_MODE || splits.getChangeNumber() > -1) {
49+
Promise.resolve().then(onReadyFromCacheCb);
50+
}
51+
4852
return {
4953
splits,
5054
segments,

src/storages/types.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,6 @@ export interface ISplitsCacheBase {
208208
// only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === -1) or contains a FF using segments or large segments
209209
usesSegments(): MaybeThenable<boolean>,
210210
clear(): MaybeThenable<boolean | void>,
211-
// should never reject or throw an exception. Instead return false by default, to avoid emitting SDK_READY_FROM_CACHE.
212-
checkCache(): MaybeThenable<boolean>,
213211
killLocally(name: string, defaultTreatment: string, changeNumber: number): MaybeThenable<boolean>,
214212
getNamesByFlagSets(flagSets: string[]): MaybeThenable<ISet<string>[]>
215213
}
@@ -226,7 +224,6 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
226224
trafficTypeExists(trafficType: string): boolean,
227225
usesSegments(): boolean,
228226
clear(): void,
229-
checkCache(): boolean,
230227
killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
231228
getNamesByFlagSets(flagSets: string[]): ISet<string>[]
232229
}
@@ -243,7 +240,6 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
243240
trafficTypeExists(trafficType: string): Promise<boolean>,
244241
usesSegments(): Promise<boolean>,
245242
clear(): Promise<boolean | void>,
246-
checkCache(): Promise<boolean>,
247243
killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
248244
getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>[]>
249245
}
@@ -504,6 +500,7 @@ export interface IStorageFactoryParams {
504500
* It is meant for emitting SDK_READY event in consumer mode, and waiting before using the storage in the synchronizer.
505501
*/
506502
onReadyCb: (error?: any) => void,
503+
onReadyFromCacheCb: (error?: any) => void,
507504
}
508505

509506
export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'PLUGGABLE';

src/sync/offline/syncTasks/fromObjectSyncTask.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { syncTaskFactory } from '../../syncTask';
77
import { ISyncTask } from '../../types';
88
import { ISettings } from '../../../types';
99
import { CONTROL } from '../../../utils/constants';
10-
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
10+
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
1111
import { SYNC_OFFLINE_DATA, ERROR_SYNC_OFFLINE_LOADING } from '../../../logger/constants';
1212

1313
/**
@@ -60,12 +60,8 @@ export function fromObjectUpdaterFactory(
6060

6161
if (startingUp) {
6262
startingUp = false;
63-
Promise.resolve(splitsCache.checkCache()).then(cacheReady => {
64-
// Emits SDK_READY_FROM_CACHE
65-
if (cacheReady) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
66-
// Emits SDK_READY
67-
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
68-
});
63+
// Emits SDK_READY
64+
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
6965
}
7066
return true;
7167
});

src/sync/polling/updaters/splitChangesUpdater.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ISplitChangesFetcher } from '../fetchers/types';
44
import { ISplit, ISplitChangesResponse, ISplitFiltersValidation } from '../../../dtos/types';
55
import { ISplitsEventEmitter } from '../../../readiness/types';
66
import { timeout } from '../../../utils/promise/timeout';
7-
import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
7+
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
88
import { ILogger } from '../../../logger/types';
99
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_NEW, SYNC_SPLITS_REMOVED, SYNC_SPLITS_SEGMENTS, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
1010
import { startsWith } from '../../../utils/lang';
@@ -153,7 +153,8 @@ export function splitChangesUpdaterFactory(
153153
*/
154154
function _splitChangesUpdater(since: number, retry = 0): Promise<boolean> {
155155
log.debug(SYNC_SPLITS_FETCH, [since]);
156-
const fetcherPromise = Promise.resolve(splitUpdateNotification ?
156+
157+
return Promise.resolve(splitUpdateNotification ?
157158
{ splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
158159
splitChangesFetcher(since, noCache, till, _promiseDecorator)
159160
)
@@ -200,15 +201,6 @@ export function splitChangesUpdaterFactory(
200201
}
201202
return false;
202203
});
203-
204-
// After triggering the requests, if we have cached splits information let's notify that to emit SDK_READY_FROM_CACHE.
205-
// Wrapping in a promise since checkCache can be async.
206-
if (splitsEventEmitter && startingUp) {
207-
Promise.resolve(splits.checkCache()).then(isCacheReady => {
208-
if (isCacheReady) splitsEventEmitter.emit(SDK_SPLITS_CACHE_LOADED);
209-
});
210-
}
211-
return fetcherPromise;
212204
}
213205

214206
let sincePromise = Promise.resolve(splits.getChangeNumber()); // `getChangeNumber` never rejects or throws error

src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { validateStorageCS, __InLocalStorageMockFactory } from '../storageCS';
1+
import { validateStorageCS } from '../storageCS';
22
import { InMemoryStorageCSFactory } from '../../../../storages/inMemory/InMemoryStorageCS';
33
import { loggerMock as log } from '../../../../logger/__tests__/sdkLogger.mock';
44

@@ -32,11 +32,6 @@ describe('storage validator for pluggable storage (client-side)', () => {
3232
expect(log.error).not.toBeCalled();
3333
});
3434

35-
test('fallbacks to mock InLocalStorage storage if the storage is InLocalStorage and the mode localhost', () => {
36-
expect(validateStorageCS({ log, mode: 'localhost', storage: mockInLocalStorageFactory })).toBe(__InLocalStorageMockFactory);
37-
expect(log.error).not.toBeCalled();
38-
});
39-
4035
test('throws error if the provided storage factory is not compatible with the mode', () => {
4136
expect(() => { validateStorageCS({ log, mode: 'consumer', storage: mockInLocalStorageFactory }); }).toThrow('A PluggableStorage instance is required on consumer mode');
4237
expect(() => { validateStorageCS({ log, mode: 'consumer_partial', storage: mockInLocalStorageFactory }); }).toThrow('A PluggableStorage instance is required on consumer mode');

src/utils/settingsValidation/storage/storageCS.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,6 @@ import { ISettings, SDKMode } from '../../../types';
33
import { ILogger } from '../../../logger/types';
44
import { ERROR_STORAGE_INVALID } from '../../../logger/constants';
55
import { LOCALHOST_MODE, STANDALONE_MODE, STORAGE_PLUGGABLE, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants';
6-
import { IStorageFactoryParams, IStorageSync } from '../../../storages/types';
7-
8-
export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSync {
9-
const result = InMemoryStorageCSFactory(params);
10-
result.splits.checkCache = () => true; // to emit SDK_READY_FROM_CACHE
11-
return result;
12-
}
13-
__InLocalStorageMockFactory.type = STORAGE_MEMORY;
146

157
/**
168
* This function validates `settings.storage` object
@@ -30,11 +22,6 @@ export function validateStorageCS(settings: { log: ILogger, storage?: any, mode:
3022
log.error(ERROR_STORAGE_INVALID);
3123
}
3224

33-
// In localhost mode with InLocalStorage, fallback to a mock InLocalStorage to emit SDK_READY_FROM_CACHE
34-
if (mode === LOCALHOST_MODE && storage.type === STORAGE_LOCALSTORAGE) {
35-
return __InLocalStorageMockFactory;
36-
}
37-
3825
if ([LOCALHOST_MODE, STANDALONE_MODE].indexOf(mode) === -1) {
3926
// Consumer modes require an async storage
4027
if (storage.type !== STORAGE_PLUGGABLE) throw new Error('A PluggableStorage instance is required on consumer mode');

0 commit comments

Comments
 (0)