diff --git a/packages/sdk/akamai-base/example/ldClient.ts b/packages/sdk/akamai-base/example/ldClient.ts index 36f92d2098..46fc1fa020 100644 --- a/packages/sdk/akamai-base/example/ldClient.ts +++ b/packages/sdk/akamai-base/example/ldClient.ts @@ -54,6 +54,9 @@ export const evaluateFlagFromCustomFeatureStore = async ( const client = init({ sdkKey: 'Your-launchdarkly-environment-client-id', featureStoreProvider: new MyCustomStoreProvider(), + options: { + cacheTtlMs: 1_000, + }, }); return client.variation(flagKey, context, defaultValue); diff --git a/packages/shared/akamai-edgeworker-sdk/__tests__/featureStore/cache.test.ts b/packages/shared/akamai-edgeworker-sdk/__tests__/featureStore/cache.test.ts new file mode 100644 index 0000000000..1e4212769b --- /dev/null +++ b/packages/shared/akamai-edgeworker-sdk/__tests__/featureStore/cache.test.ts @@ -0,0 +1,113 @@ +import { AsyncStoreFacade, LDFeatureStore } from '@launchdarkly/js-server-sdk-common'; + +import { EdgeFeatureStore, EdgeProvider } from '../../src/featureStore'; +import * as testData from '../testData.json'; + +describe('EdgeFeatureStore', () => { + const sdkKey = 'sdkKey'; + const mockLogger = { + error: jest.fn(), + warn: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + const mockEdgeProvider: EdgeProvider = { + get: jest.fn(), + }; + const mockGet = mockEdgeProvider.get as jest.Mock; + let featureStore: LDFeatureStore; + let asyncFeatureStore: AsyncStoreFacade; + + describe('with infinite cache', () => { + beforeEach(() => { + mockGet.mockImplementation(() => Promise.resolve(JSON.stringify(testData))); + featureStore = new EdgeFeatureStore( + mockEdgeProvider, + sdkKey, + 'MockEdgeProvider', + mockLogger, + 0, + ); + asyncFeatureStore = new AsyncStoreFacade(featureStore); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('will cache the initial request', async () => { + await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1'); + await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1'); + await asyncFeatureStore.all({ namespace: 'features' }); + + expect(mockGet).toHaveBeenCalledTimes(1); + }); + }); + + describe('with cache disabled', () => { + beforeEach(() => { + mockGet.mockImplementation(() => Promise.resolve(JSON.stringify(testData))); + featureStore = new EdgeFeatureStore( + mockEdgeProvider, + sdkKey, + 'MockEdgeProvider', + mockLogger, + -1, + ); + asyncFeatureStore = new AsyncStoreFacade(featureStore); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('caches nothing', async () => { + await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1'); + await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1'); + await asyncFeatureStore.all({ namespace: 'features' }); + + expect(mockGet).toHaveBeenCalledTimes(3); + }); + }); + + describe('with finite cache', () => { + beforeEach(() => { + mockGet.mockImplementation(() => Promise.resolve(JSON.stringify(testData))); + featureStore = new EdgeFeatureStore( + mockEdgeProvider, + sdkKey, + 'MockEdgeProvider', + mockLogger, + 100, + ); + asyncFeatureStore = new AsyncStoreFacade(featureStore); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('expires after configured duration', async () => { + jest.spyOn(Date, 'now').mockImplementation(() => 0); + await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1'); + await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1'); + await asyncFeatureStore.all({ namespace: 'features' }); + + expect(mockGet).toHaveBeenCalledTimes(1); + + jest.spyOn(Date, 'now').mockImplementation(() => 99); + await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1'); + await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1'); + await asyncFeatureStore.all({ namespace: 'features' }); + + expect(mockGet).toHaveBeenCalledTimes(1); + + jest.spyOn(Date, 'now').mockImplementation(() => 100); + await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1'); + await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1'); + await asyncFeatureStore.all({ namespace: 'features' }); + + expect(mockGet).toHaveBeenCalledTimes(2); + }); + }); +}); diff --git a/packages/shared/akamai-edgeworker-sdk/__tests__/featureStore/cacheableStore.test.ts b/packages/shared/akamai-edgeworker-sdk/__tests__/featureStore/cacheableStore.test.ts deleted file mode 100644 index 593e9eb026..0000000000 --- a/packages/shared/akamai-edgeworker-sdk/__tests__/featureStore/cacheableStore.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { EdgeProvider } from '../../src/featureStore'; -import CacheableStoreProvider from '../../src/featureStore/cacheableStoreProvider'; -import * as testData from '../testData.json'; - -describe('given a mock edge provider with test data', () => { - const mockEdgeProvider: EdgeProvider = { - get: jest.fn(), - }; - const mockGet = mockEdgeProvider.get as jest.Mock; - - beforeEach(() => { - jest.useFakeTimers(); - mockGet.mockImplementation(() => Promise.resolve(JSON.stringify(testData))); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - describe('without cache TTL', () => { - it('caches initial request', async () => { - const cacheProvider = new CacheableStoreProvider(mockEdgeProvider, 'rootKey'); - await cacheProvider.get('rootKey'); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(1); - }); - - it('can force a refresh', async () => { - const cacheProvider = new CacheableStoreProvider(mockEdgeProvider, 'rootKey'); - await cacheProvider.get('rootKey'); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(1); - - await cacheProvider.prefetchPayloadFromOriginStore(); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(2); - }); - }); - - describe('with infinite cache ttl', () => { - it('caches initial request', async () => { - const cacheProvider = new CacheableStoreProvider(mockEdgeProvider, 'rootKey', 0); - await cacheProvider.get('rootKey'); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(1); - }); - - it('does not reset on prefetch', async () => { - const cacheProvider = new CacheableStoreProvider(mockEdgeProvider, 'rootKey', 0); - await cacheProvider.get('rootKey'); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(1); - - await cacheProvider.prefetchPayloadFromOriginStore(); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(1); - }); - }); - - describe('with finite cache ttl', () => { - it('caches initial request', async () => { - const cacheProvider = new CacheableStoreProvider(mockEdgeProvider, 'rootKey', 50); - await cacheProvider.get('rootKey'); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(1); - }); - - it('caches expires after duration', async () => { - jest.spyOn(Date, 'now').mockImplementation(() => 0); - const cacheProvider = new CacheableStoreProvider(mockEdgeProvider, 'rootKey', 50); - await cacheProvider.get('rootKey'); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(1); - - jest.spyOn(Date, 'now').mockImplementation(() => 20); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(1); - - jest.spyOn(Date, 'now').mockImplementation(() => 50); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(2); - }); - - it('prefetch respects cache TTL', async () => { - jest.spyOn(Date, 'now').mockImplementation(() => 0); - const cacheProvider = new CacheableStoreProvider(mockEdgeProvider, 'rootKey', 50); - await cacheProvider.get('rootKey'); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(1); - - await cacheProvider.prefetchPayloadFromOriginStore(); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(1); - - jest.spyOn(Date, 'now').mockImplementation(() => 50); - await cacheProvider.prefetchPayloadFromOriginStore(); - await cacheProvider.get('rootKey'); - expect(mockGet).toHaveBeenCalledTimes(2); - }); - }); -}); diff --git a/packages/shared/akamai-edgeworker-sdk/__tests__/featureStore/index.test.ts b/packages/shared/akamai-edgeworker-sdk/__tests__/featureStore/index.test.ts index ce243c6ed3..df51cd6107 100644 --- a/packages/shared/akamai-edgeworker-sdk/__tests__/featureStore/index.test.ts +++ b/packages/shared/akamai-edgeworker-sdk/__tests__/featureStore/index.test.ts @@ -21,7 +21,13 @@ describe('EdgeFeatureStore', () => { beforeEach(() => { mockGet.mockImplementation(() => Promise.resolve(JSON.stringify(testData))); - featureStore = new EdgeFeatureStore(mockEdgeProvider, sdkKey, 'MockEdgeProvider', mockLogger); + featureStore = new EdgeFeatureStore( + mockEdgeProvider, + sdkKey, + 'MockEdgeProvider', + mockLogger, + 0, + ); asyncFeatureStore = new AsyncStoreFacade(featureStore); }); diff --git a/packages/shared/akamai-edgeworker-sdk/__tests__/index.test.ts b/packages/shared/akamai-edgeworker-sdk/__tests__/index.test.ts index 9b0264f820..411905af00 100644 --- a/packages/shared/akamai-edgeworker-sdk/__tests__/index.test.ts +++ b/packages/shared/akamai-edgeworker-sdk/__tests__/index.test.ts @@ -6,6 +6,7 @@ const createClient = (sdkKey: string, mockLogger: LDLogger, mockEdgeProvider: Ed sdkKey, options: { logger: mockLogger, + cacheTtlMs: 0, }, featureStoreProvider: mockEdgeProvider, platformName: 'platform-name', @@ -40,14 +41,6 @@ describe('EdgeWorker', () => { it('should call edge providers get method only once', async () => { const client = createClient(sdkKey, mockLogger, mockEdgeProvider); await client.waitForInitialization(); - await client.allFlagsState({ kind: 'multi', l: { key: 'key' } }); - - expect(mockGet).toHaveBeenCalledTimes(1); - }); - - it('should call edge providers get method only 3 times', async () => { - const client = createClient(sdkKey, mockLogger, mockEdgeProvider); - await client.waitForInitialization(); const context: LDMultiKindContext = { kind: 'multi', l: { key: 'key' } }; @@ -55,7 +48,7 @@ describe('EdgeWorker', () => { await client.variation('testFlag1', context, false); await client.variationDetail('testFlag1', context, false); - expect(mockGet).toHaveBeenCalledTimes(3); + expect(mockGet).toHaveBeenCalledTimes(1); }); it('should successfully return data for allFlagsState', async () => { diff --git a/packages/shared/akamai-edgeworker-sdk/__tests__/platform/requests.test.ts b/packages/shared/akamai-edgeworker-sdk/__tests__/platform/requests.test.ts index e70836b070..fbd2555992 100644 --- a/packages/shared/akamai-edgeworker-sdk/__tests__/platform/requests.test.ts +++ b/packages/shared/akamai-edgeworker-sdk/__tests__/platform/requests.test.ts @@ -5,7 +5,7 @@ import EdgeRequests from '../../src/platform/requests'; const TEXT_RESPONSE = ''; const JSON_RESPONSE = {}; -describe('given a default instance of requets', () => { +describe('given a default instance of requests', () => { const requests = new EdgeRequests(); describe('fetch', () => { diff --git a/packages/shared/akamai-edgeworker-sdk/__tests__/utils/validateOptions.test.ts b/packages/shared/akamai-edgeworker-sdk/__tests__/utils/validateOptions.test.ts index 4126a9fe81..8953cb6a4a 100644 --- a/packages/shared/akamai-edgeworker-sdk/__tests__/utils/validateOptions.test.ts +++ b/packages/shared/akamai-edgeworker-sdk/__tests__/utils/validateOptions.test.ts @@ -26,7 +26,7 @@ const mockOptions = ({ }) => { const mockLogger = logger ?? BasicLogger.get(); const mockFeatureStore = - featureStore ?? new EdgeFeatureStore(edgeProvider, SDK_KEY, 'validationTest', mockLogger); + featureStore ?? new EdgeFeatureStore(edgeProvider, SDK_KEY, 'validationTest', mockLogger, 0); return { featureStore: allowEmptyFS ? undefined : mockFeatureStore, diff --git a/packages/shared/akamai-edgeworker-sdk/src/api/LDClient.ts b/packages/shared/akamai-edgeworker-sdk/src/api/LDClient.ts index b29d2a9221..b5f9cf9fec 100644 --- a/packages/shared/akamai-edgeworker-sdk/src/api/LDClient.ts +++ b/packages/shared/akamai-edgeworker-sdk/src/api/LDClient.ts @@ -2,15 +2,9 @@ import { LDClientImpl, LDClient as LDClientType, - LDContext, - LDEvaluationDetail, - LDFlagsState, - LDFlagsStateOptions, - LDFlagValue, LDOptions, } from '@launchdarkly/js-server-sdk-common'; -import CacheableStoreProvider from '../featureStore/cacheableStoreProvider'; import EdgePlatform from '../platform'; import { createCallbacks, createOptions } from '../utils'; @@ -20,18 +14,10 @@ export interface CustomLDOptions extends LDOptions {} * The LaunchDarkly Akamai SDK edge client object. */ class LDClient extends LDClientImpl { - private _cacheableStoreProvider!: CacheableStoreProvider; - // sdkKey is only used to query featureStore, not to initialize with LD servers - constructor( - sdkKey: string, - platform: EdgePlatform, - options: LDOptions, - storeProvider: CacheableStoreProvider, - ) { + constructor(sdkKey: string, platform: EdgePlatform, options: LDOptions) { const finalOptions = createOptions(options); super(sdkKey, platform, finalOptions, createCallbacks(finalOptions.logger)); - this._cacheableStoreProvider = storeProvider; } override initialized(): boolean { @@ -39,39 +25,10 @@ class LDClient extends LDClientImpl { } override waitForInitialization(): Promise { - // we need to resolve the promise immediately because Akamai's runtime doesnt - // have a setimeout so everything executes synchronously. + // we need to resolve the promise immediately because Akamai's runtime doesn't + // have a setTimeout so everything executes synchronously. return Promise.resolve(this); } - - override async variation( - key: string, - context: LDContext, - defaultValue: LDFlagValue, - callback?: (err: any, res: LDFlagValue) => void, - ): Promise { - await this._cacheableStoreProvider.prefetchPayloadFromOriginStore(); - return super.variation(key, context, defaultValue, callback); - } - - override async variationDetail( - key: string, - context: LDContext, - defaultValue: LDFlagValue, - callback?: (err: any, res: LDEvaluationDetail) => void, - ): Promise { - await this._cacheableStoreProvider.prefetchPayloadFromOriginStore(); - return super.variationDetail(key, context, defaultValue, callback); - } - - override async allFlagsState( - context: LDContext, - options?: LDFlagsStateOptions, - callback?: (err: Error | null, res: LDFlagsState) => void, - ): Promise { - await this._cacheableStoreProvider.prefetchPayloadFromOriginStore(); - return super.allFlagsState(context, options, callback); - } } export default LDClient; diff --git a/packages/shared/akamai-edgeworker-sdk/src/featureStore/cache.ts b/packages/shared/akamai-edgeworker-sdk/src/featureStore/cache.ts new file mode 100644 index 0000000000..3e8b021b3a --- /dev/null +++ b/packages/shared/akamai-edgeworker-sdk/src/featureStore/cache.ts @@ -0,0 +1,43 @@ +interface CacheItem { + value: any; + expiration: number; +} + +export default class Cache { + private _cache: CacheItem | undefined; + + constructor(private readonly _cacheTtlMs: number) {} + + get(): any | undefined { + // If the cacheTtlMs is less than 0, the cache is disabled. + if (this._cacheTtlMs < 0) { + return undefined; + } + + // If there isn't a cached item, we must return undefined. + if (this._cache === undefined) { + return undefined; + } + + // A cacheTtlMs of 0 is infinite caching, so we can always return the + // value. + // + // We also want to return the value if it hasn't expired. + if (this._cacheTtlMs === 0 || Date.now() < this._cache.expiration) { + return this._cache.value; + } + + // If you have gotten this far, the cache is stale. Better to drop it as a + // way to short-circuit checking the freshness again. + this._cache = undefined; + + return undefined; + } + + set(value: any): void { + this._cache = { + value, + expiration: Date.now() + this._cacheTtlMs, + }; + } +} diff --git a/packages/shared/akamai-edgeworker-sdk/src/featureStore/cacheableStoreProvider.ts b/packages/shared/akamai-edgeworker-sdk/src/featureStore/cacheableStoreProvider.ts deleted file mode 100644 index 7839d04ddc..0000000000 --- a/packages/shared/akamai-edgeworker-sdk/src/featureStore/cacheableStoreProvider.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { EdgeProvider } from '.'; - -/** - * Wraps around an edge provider to cache a copy of the SDK payload locally. - * - * If a cacheTtlMs is specified, then the cacheable store provider will cache - * results for that specified duration. If the data lookup fails after that - * interval, previously stored values will be retained. The lookup will be - * retried again after the TTL. - * - * If no cacheTtlMs is specified, the cache will be stored for the lifetime of - * the object. The cache can be manually refreshed by calling - * `prefetchPayloadFromOriginStore`. - * - * The wrapper is necessary to ensure that we don't make redundant sub-requests - * from Akamai to fetch an entire environment payload. At the time of this writing, - * the Akamai documentation (https://techdocs.akamai.com/edgeworkers/docs/resource-tier-limitations) - * limits the number of sub-requests to: - * - * - 2 for basic compute - * - 4 for dynamic compute - * - 10 for enterprise - */ -export default class CacheableStoreProvider implements EdgeProvider { - cache: Promise | null | undefined; - cachedAt: number | undefined; - - constructor( - private readonly _edgeProvider: EdgeProvider, - private readonly _rootKey: string, - private readonly _cacheTtlMs?: number, - ) {} - - /** - * Get data from the edge provider feature store. - * @param rootKey - * @returns - */ - async get(rootKey: string): Promise { - if (!this._isCacheValid()) { - this.cache = this._edgeProvider.get(rootKey); - this.cachedAt = Date.now(); - } - - return this.cache; - } - - /** - * Fetches environment payload data from the origin in accordance with the caching configuration. - * - * You should only call this function within a feature store to pre-fetch and cache payload data in environments - * where its expensive to make multiple outbound requests to the origin - * @param rootKey - * @returns - */ - async prefetchPayloadFromOriginStore(rootKey?: string): Promise { - if (this._cacheTtlMs === undefined) { - this.cache = undefined; // clear the cache so that new data can be fetched from the origin - } - - return this.get(rootKey || this._rootKey); - } - - /** - * Internal helper to determine if the cached values are still considered valid. - */ - private _isCacheValid(): boolean { - // If we don't have a cache, or we don't know how old the cache is, we have - // to consider it is invalid. - if (!this.cache || this.cachedAt === undefined) { - return false; - } - - // If the cache provider was configured without a TTL, then the cache is - // always considered valid. - if (!this._cacheTtlMs) { - return true; - } - - // Otherwise, it all depends on the time. - return Date.now() - this.cachedAt < this._cacheTtlMs; - } -} diff --git a/packages/shared/akamai-edgeworker-sdk/src/featureStore/index.ts b/packages/shared/akamai-edgeworker-sdk/src/featureStore/index.ts index 18960e1f75..a87bcad193 100644 --- a/packages/shared/akamai-edgeworker-sdk/src/featureStore/index.ts +++ b/packages/shared/akamai-edgeworker-sdk/src/featureStore/index.ts @@ -8,6 +8,8 @@ import type { } from '@launchdarkly/js-server-sdk-common'; import { deserializePoll, noop } from '@launchdarkly/js-server-sdk-common'; +import Cache from './cache'; + export interface EdgeProvider { get: (rootKey: string) => Promise; } @@ -21,14 +23,17 @@ export const buildRootKey = (sdkKey: string) => `LD-Env-${sdkKey}`; export class EdgeFeatureStore implements LDFeatureStore { private readonly _rootKey: string; + private _cache: Cache; constructor( private readonly _edgeProvider: EdgeProvider, private readonly _sdkKey: string, private readonly _description: string, private _logger: LDLogger, + _cacheTtlMs: number, ) { this._rootKey = buildRootKey(this._sdkKey); + this._cache = new Cache(_cacheTtlMs); } async get( @@ -41,23 +46,17 @@ export class EdgeFeatureStore implements LDFeatureStore { this._logger.debug(`Requesting ${dataKey} from ${this._rootKey}.${kindKey}`); try { - const i = await this._edgeProvider.get(this._rootKey); - - if (!i) { - throw new Error(`${this._rootKey}.${kindKey} is not found in KV.`); - } - - const item = deserializePoll(i); - if (!item) { - throw new Error(`Error deserializing ${kindKey}`); + const storePayload = await this._getStorePayload(); + if (!storePayload) { + throw new Error(`Error deserializing ${this._rootKey}`); } switch (namespace) { case 'features': - callback(item.flags[dataKey]); + callback(storePayload.flags[dataKey]); break; case 'segments': - callback(item.segments[dataKey]); + callback(storePayload.segments[dataKey]); break; default: callback(null); @@ -73,22 +72,17 @@ export class EdgeFeatureStore implements LDFeatureStore { const kindKey = namespace === 'features' ? 'flags' : namespace; this._logger.debug(`Requesting all from ${this._rootKey}.${kindKey}`); try { - const i = await this._edgeProvider.get(this._rootKey); - if (!i) { + const storePayload = await this._getStorePayload(); + if (!storePayload) { throw new Error(`${this._rootKey}.${kindKey} is not found in KV.`); } - const item = deserializePoll(i); - if (!item) { - throw new Error(`Error deserializing ${kindKey}`); - } - switch (namespace) { case 'features': - callback(item.flags); + callback(storePayload.flags); break; case 'segments': - callback(item.segments); + callback(storePayload.segments); break; default: throw new Error(`Unsupported DataKind: ${namespace}`); @@ -99,6 +93,37 @@ export class EdgeFeatureStore implements LDFeatureStore { } } + // This method is used to retrieve the environment payload from the edge + // provider. It will cache the payload for the duration of the cacheTtlMs. + + /** + * This method is used to retrieve the environment payload from the edge + * provider. It will cache the payload for the duration of the cacheTtlMs. + * + * @returns + */ + private async _getStorePayload(): Promise> { + let payload = this._cache.get(); + if (payload !== undefined) { + return payload; + } + + const providerData = await this._edgeProvider.get(this._rootKey); + + if (!providerData) { + throw new Error(`${this._rootKey} is not found in KV.`); + } + + payload = deserializePoll(providerData); + if (!payload) { + throw new Error(`Error deserializing ${this._rootKey}`); + } + + this._cache.set(payload); + + return payload; + } + async initialized(callback: (isInitialized: boolean) => void = noop): Promise { const config = await this._edgeProvider.get(this._rootKey); const result = config !== null; diff --git a/packages/shared/akamai-edgeworker-sdk/src/index.ts b/packages/shared/akamai-edgeworker-sdk/src/index.ts index e38d0280b8..36c4225aae 100644 --- a/packages/shared/akamai-edgeworker-sdk/src/index.ts +++ b/packages/shared/akamai-edgeworker-sdk/src/index.ts @@ -1,8 +1,7 @@ import { BasicLogger, LDOptions as LDOptionsCommon } from '@launchdarkly/js-server-sdk-common'; import LDClient from './api/LDClient'; -import { buildRootKey, EdgeFeatureStore, EdgeProvider } from './featureStore'; -import CacheableStoreProvider from './featureStore/cacheableStoreProvider'; +import { EdgeFeatureStore, EdgeProvider } from './featureStore'; import EdgePlatform from './platform'; import createPlatformInfo from './platform/info'; import { validateOptions } from './utils'; @@ -51,12 +50,13 @@ export const init = (params: BaseSDKParams): LDClient => { const logger = inputOptions.logger ?? BasicLogger.get(); const { cacheTtlMs, ...options } = inputOptions as any; - const cachableStoreProvider = new CacheableStoreProvider( + const featureStore = new EdgeFeatureStore( featureStoreProvider, - buildRootKey(sdkKey), - cacheTtlMs, + sdkKey, + 'Akamai', + logger, + cacheTtlMs ?? 100, ); - const featureStore = new EdgeFeatureStore(cachableStoreProvider, sdkKey, 'Akamai', logger); const ldOptions: LDOptionsCommon = { featureStore, @@ -68,5 +68,5 @@ export const init = (params: BaseSDKParams): LDClient => { validateOptions(params.sdkKey, ldOptions); const platform = createPlatformInfo(platformName, sdkName, sdkVersion); - return new LDClient(sdkKey, new EdgePlatform(platform), ldOptions, cachableStoreProvider); + return new LDClient(sdkKey, new EdgePlatform(platform), ldOptions); };