Skip to content

Commit 34c4bce

Browse files
Revert "Add onReadyFromCacheCb to storage factory params for code cleanup"
This reverts commit 38092dc.
1 parent 270d811 commit 34c4bce

File tree

11 files changed

+76
-21
lines changed

11 files changed

+76
-21
lines changed

src/sdkFactory/index.ts

Lines changed: 1 addition & 4 deletions
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, SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
10+
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
1111
import { objectAssign } from '../utils/lang/objectAssign';
1212
import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
1313
import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
@@ -52,9 +52,6 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
5252
readiness.splits.emit(SDK_SPLITS_ARRIVED);
5353
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
5454
},
55-
onReadyFromCacheCb: () => {
56-
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
57-
}
5855
});
5956
// @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
6057
const clients: Record<string, IBasicClient> = {};

src/storages/AbstractSplitsCacheAsync.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ 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+
3139
/**
3240
* Kill `name` split and set `defaultTreatment` and `changeNumber`.
3341
* Used for SPLIT_KILL push notifications.

src/storages/AbstractSplitsCacheSync.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ 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+
5159
/**
5260
* Kill `name` split and set `defaultTreatment` and `changeNumber`.
5361
* Used for SPLIT_KILL push notifications.

src/storages/inLocalStorage/SplitsCacheInLocal.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,15 @@ 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+
220229
/**
221230
* Clean Splits cache if its `lastUpdated` timestamp is older than the given `expirationTimestamp`,
222231
*
@@ -241,7 +250,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
241250
this.updateNewFilter = true;
242251

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

246255
} catch (e) {
247256
this.log.error(LOG_PREFIX + e);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,16 @@ 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+
3436
expect(cache.getChangeNumber() === -1).toBe(true);
3537

38+
expect(cache.checkCache()).toBe(false); // checkCache should return false until localstorage has data.
39+
3640
cache.setChangeNumber(123);
3741

42+
expect(cache.checkCache()).toBe(true); // checkCache should return true once localstorage has data.
43+
3844
expect(cache.getChangeNumber() === 123).toBe(true);
3945

4046
});

src/storages/inLocalStorage/index.ts

Lines changed: 2 additions & 8 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, LOCALHOST_MODE, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
15+
import { DEBUG, 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 { onReadyFromCacheCb, settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
39+
const { 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;
@@ -55,12 +55,6 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
5555
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
5656
uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
5757

58-
init() {
59-
if (settings.mode === LOCALHOST_MODE || splits.getChangeNumber() > -1) {
60-
Promise.resolve().then(onReadyFromCacheCb);
61-
}
62-
},
63-
6458
destroy() {
6559
this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
6660
this.segments = new MySegmentsCacheInMemory();

src/storages/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ 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>,
211213
killLocally(name: string, defaultTreatment: string, changeNumber: number): MaybeThenable<boolean>,
212214
getNamesByFlagSets(flagSets: string[]): MaybeThenable<ISet<string>[]>
213215
}
@@ -224,6 +226,7 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
224226
trafficTypeExists(trafficType: string): boolean,
225227
usesSegments(): boolean,
226228
clear(): void,
229+
checkCache(): boolean,
227230
killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
228231
getNamesByFlagSets(flagSets: string[]): ISet<string>[]
229232
}
@@ -240,6 +243,7 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
240243
trafficTypeExists(trafficType: string): Promise<boolean>,
241244
usesSegments(): Promise<boolean>,
242245
clear(): Promise<boolean | void>,
246+
checkCache(): Promise<boolean>,
243247
killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
244248
getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>[]>
245249
}
@@ -501,7 +505,6 @@ export interface IStorageFactoryParams {
501505
* It is meant for emitting SDK_READY event in consumer mode, and waiting before using the storage in the synchronizer.
502506
*/
503507
onReadyCb: (error?: any) => void,
504-
onReadyFromCacheCb: (error?: any) => void,
505508
}
506509

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

src/sync/offline/syncTasks/fromObjectSyncTask.ts

Lines changed: 7 additions & 3 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 } from '../../../readiness/constants';
10+
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
1111
import { SYNC_OFFLINE_DATA, ERROR_SYNC_OFFLINE_LOADING } from '../../../logger/constants';
1212

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

6161
if (startingUp) {
6262
startingUp = false;
63-
// Emits SDK_READY
64-
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
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+
});
6569
}
6670
return true;
6771
});

src/sync/polling/updaters/splitChangesUpdater.ts

Lines changed: 11 additions & 3 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 } from '../../../readiness/constants';
7+
import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED } 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,8 +153,7 @@ export function splitChangesUpdaterFactory(
153153
*/
154154
function _splitChangesUpdater(since: number, retry = 0): Promise<boolean> {
155155
log.debug(SYNC_SPLITS_FETCH, [since]);
156-
157-
return Promise.resolve(splitUpdateNotification ?
156+
const fetcherPromise = Promise.resolve(splitUpdateNotification ?
158157
{ splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
159158
splitChangesFetcher(since, noCache, till, _promiseDecorator)
160159
)
@@ -201,6 +200,15 @@ export function splitChangesUpdaterFactory(
201200
}
202201
return false;
203202
});
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;
204212
}
205213

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

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

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

@@ -32,6 +32,11 @@ 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+
3540
test('throws error if the provided storage factory is not compatible with the mode', () => {
3641
expect(() => { validateStorageCS({ log, mode: 'consumer', storage: mockInLocalStorageFactory }); }).toThrow('A PluggableStorage instance is required on consumer mode');
3742
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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ 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;
614

715
/**
816
* This function validates `settings.storage` object
@@ -22,6 +30,11 @@ export function validateStorageCS(settings: { log: ILogger, storage?: any, mode:
2230
log.error(ERROR_STORAGE_INVALID);
2331
}
2432

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+
2538
if ([LOCALHOST_MODE, STANDALONE_MODE].indexOf(mode) === -1) {
2639
// Consumer modes require an async storage
2740
if (storage.type !== STORAGE_PLUGGABLE) throw new Error('A PluggableStorage instance is required on consumer mode');

0 commit comments

Comments
 (0)