Skip to content

Commit c270290

Browse files
Revert "Revert lazy init in storage"
This reverts commit 57ba406.
1 parent d59aea5 commit c270290

File tree

5 files changed

+45
-34
lines changed

5 files changed

+45
-34
lines changed

src/sdkFactory/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
102102
// We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
103103
validateAndTrackApiKey(log, settings.core.authorizationKey);
104104
readiness.init();
105+
storage.init && storage.init();
105106
uniqueKeysTracker && uniqueKeysTracker.start();
106107
syncManager && syncManager.start();
107108
signalListener && signalListener.start();

src/storages/inRedis/RedisAdapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const DEFAULT_OPTIONS = {
2020
const DEFAULT_LIBRARY_OPTIONS = {
2121
enableOfflineQueue: false,
2222
connectTimeout: DEFAULT_OPTIONS.connectionTimeout,
23-
lazyConnect: false
23+
lazyConnect: false // @TODO true to avoid side-effects on instantiation
2424
};
2525

2626
interface IRedisCommand {

src/storages/pluggable/__tests__/index.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('PLUGGABLE STORAGE', () => {
2828
test('creates a storage instance', async () => {
2929
const storageFactory = PluggableStorage({ prefix, wrapper: wrapperMock });
3030
const storage = storageFactory(internalSdkParams);
31+
storage.init();
3132

3233
assertStorageInterface(storage); // the instance must implement the storage interface
3334
expect(wrapperMock.connect).toBeCalledTimes(1); // wrapper connect method should be called once when storage is created
@@ -74,6 +75,7 @@ describe('PLUGGABLE STORAGE', () => {
7475
test('creates a storage instance for partial consumer mode (events and impressions cache in memory)', async () => {
7576
const storageFactory = PluggableStorage({ prefix, wrapper: wrapperMock });
7677
const storage = storageFactory({ ...internalSdkParams, settings: { ...internalSdkParams.settings, mode: CONSUMER_PARTIAL_MODE } });
78+
storage.init();
7779

7880
assertStorageInterface(storage);
7981
expect(wrapperMock.connect).toBeCalledTimes(1);
@@ -102,6 +104,7 @@ describe('PLUGGABLE STORAGE', () => {
102104
// Create storage instance. Wrapper is pollute but doesn't have filter query key, so it should clear the cache
103105
await new Promise(resolve => {
104106
storage = storageFactory({ onReadyCb: resolve, settings: { ...fullSettings, mode: undefined } });
107+
storage.init();
105108
});
106109

107110
// Assert that expected caches are present
@@ -121,6 +124,7 @@ describe('PLUGGABLE STORAGE', () => {
121124
// Create storage instance. This time the wrapper has the current filter query key, so it should not clear the cache
122125
await new Promise(resolve => {
123126
storage = storageFactory({ onReadyCb: resolve, settings: { ...fullSettings, mode: undefined } });
127+
storage.init();
124128
});
125129

126130
// Assert that cache was not cleared

src/storages/pluggable/index.ts

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IPluggableStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams, ITelemetryCacheAsync } from '../types';
1+
import { IPluggableStorageWrapper, IStorageAsyncFactory, IStorageFactoryParams, ITelemetryCacheAsync } from '../types';
22

33
import { KeyBuilderSS } from '../KeyBuilderSS';
44
import { SplitsCachePluggable } from './SplitsCachePluggable';
@@ -62,11 +62,12 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
6262

6363
const prefix = validatePrefix(options.prefix);
6464

65-
function PluggableStorageFactory(params: IStorageFactoryParams): IStorageAsync {
65+
function PluggableStorageFactory(params: IStorageFactoryParams) {
6666
const { onReadyCb, settings, settings: { log, mode, sync: { impressionsMode }, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
6767
const metadata = metadataBuilder(settings);
6868
const keys = new KeyBuilderSS(prefix, metadata);
6969
const wrapper = wrapperAdapter(log, options.wrapper);
70+
let connectPromise: Promise<void>;
7071

7172
const isSyncronizer = mode === undefined; // If mode is not defined, the synchronizer is running
7273
const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
@@ -89,35 +90,6 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
8990
new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
9091
undefined;
9192

92-
// Connects to wrapper and emits SDK_READY event on main client
93-
const connectPromise = wrapper.connect().then(() => {
94-
if (isSyncronizer) {
95-
// In standalone or producer mode, clear storage if SDK key or feature flag filter has changed
96-
return wrapper.get(keys.buildHashKey()).then((hash) => {
97-
const currentHash = getStorageHash(settings);
98-
if (hash !== currentHash) {
99-
log.info(LOG_PREFIX + 'Storage HASH has changed (SDK key, flags filter criteria or flags spec version was modified). Clearing cache');
100-
return wrapper.getKeysByPrefix(`${keys.prefix}.`).then(storageKeys => {
101-
return Promise.all(storageKeys.map(storageKey => wrapper.del(storageKey)));
102-
}).then(() => wrapper.set(keys.buildHashKey(), currentHash));
103-
}
104-
}).then(() => {
105-
onReadyCb();
106-
});
107-
} else {
108-
// Start periodic flush of async storages if not running synchronizer (producer mode)
109-
if (impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
110-
if (uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
111-
if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig) (telemetry as ITelemetryCacheAsync).recordConfig();
112-
113-
onReadyCb();
114-
}
115-
}).catch((e) => {
116-
e = e || new Error('Error connecting wrapper');
117-
onReadyCb(e);
118-
return e; // Propagate error for shared clients
119-
});
120-
12193
return {
12294
splits: new SplitsCachePluggable(log, keys, wrapper, settings.sync.__splitFiltersValidation),
12395
segments: new SegmentsCachePluggable(log, keys, wrapper),
@@ -127,6 +99,39 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
12799
telemetry,
128100
uniqueKeys: uniqueKeysCache,
129101

102+
init() {
103+
if (connectPromise) return connectPromise;
104+
105+
// Connects to wrapper and emits SDK_READY event on main client
106+
return connectPromise = wrapper.connect().then(() => {
107+
if (isSyncronizer) {
108+
// In standalone or producer mode, clear storage if SDK key or feature flag filter has changed
109+
return wrapper.get(keys.buildHashKey()).then((hash) => {
110+
const currentHash = getStorageHash(settings);
111+
if (hash !== currentHash) {
112+
log.info(LOG_PREFIX + 'Storage HASH has changed (SDK key, flags filter criteria or flags spec version was modified). Clearing cache');
113+
return wrapper.getKeysByPrefix(`${keys.prefix}.`).then(storageKeys => {
114+
return Promise.all(storageKeys.map(storageKey => wrapper.del(storageKey)));
115+
}).then(() => wrapper.set(keys.buildHashKey(), currentHash));
116+
}
117+
}).then(() => {
118+
onReadyCb();
119+
});
120+
} else {
121+
// Start periodic flush of async storages if not running synchronizer (producer mode)
122+
if (impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
123+
if (uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
124+
if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig) (telemetry as ITelemetryCacheAsync).recordConfig();
125+
126+
onReadyCb();
127+
}
128+
}).catch((e) => {
129+
e = e || new Error('Error connecting wrapper');
130+
onReadyCb(e);
131+
return e; // Propagate error for shared clients
132+
});
133+
},
134+
130135
// Stop periodic flush and disconnect the underlying storage
131136
destroy() {
132137
return Promise.all(isSyncronizer ? [] : [
@@ -136,8 +141,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
136141
},
137142

138143
// emits SDK_READY event on shared clients and returns a reference to the storage
139-
shared(_, onReadyCb) {
140-
connectPromise.then(onReadyCb);
144+
shared(_: string, onReadyCb: (error?: any) => void) {
145+
this.init().then(onReadyCb);
141146

142147
return {
143148
...this,

src/storages/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ export interface IStorageBase<
456456
events: TEventsCache,
457457
telemetry?: TTelemetryCache,
458458
uniqueKeys?: TUniqueKeysCache,
459+
init?: () => void | Promise<void>,
459460
destroy(): void | Promise<void>,
460461
shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this
461462
}

0 commit comments

Comments
 (0)