Skip to content

Commit ae47860

Browse files
feat: Improve KV data caching and deserialization (#903)
**Requirements** - [X] I have added test coverage for new or changed functionality - [X] I have followed the repository's [pull request submission guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests) - [X] I have validated my changes against all supported platform versions **Describe the solution you've provided** Improve KV data caching and deserialization
1 parent 9474862 commit ae47860

File tree

1 file changed

+51
-27
lines changed

1 file changed

+51
-27
lines changed

packages/sdk/fastly/src/api/EdgeFeatureStore.ts

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface EdgeProvider {
1414

1515
export class EdgeFeatureStore implements LDFeatureStore {
1616
private readonly _rootKey: string;
17-
private _kvData: string | null = null;
17+
private _deserializedPromise: Promise<LDFeatureStoreDataStorage | null> | null = null;
1818

1919
constructor(
2020
private readonly _edgeProvider: EdgeProvider,
@@ -30,11 +30,36 @@ export class EdgeFeatureStore implements LDFeatureStore {
3030
* has a limit of 32 backend requests (including requests to fetch the KV data).
3131
* https://docs.fastly.com/products/compute-resource-limits
3232
*/
33-
private async _getKVData(): Promise<string | null | undefined> {
34-
if (!this._kvData) {
35-
this._kvData = await this._edgeProvider.get(this._rootKey);
33+
private async _getKVData(): Promise<LDFeatureStoreDataStorage | null> {
34+
if (!this._deserializedPromise) {
35+
this._deserializedPromise = (async (): Promise<LDFeatureStoreDataStorage | null> => {
36+
this._logger.debug('No cached data found, loading from KV store');
37+
const kvData = await this._edgeProvider.get(this._rootKey);
38+
if (!kvData) {
39+
this._logger.debug('No data found in KV store');
40+
return null;
41+
}
42+
43+
this._logger.debug('Deserializing KV store data');
44+
const deserialized = deserializePoll(kvData);
45+
if (!deserialized) {
46+
this._logger.debug('Failed to deserialize KV store data');
47+
return null;
48+
}
49+
50+
// Convert FlagsAndSegments to LDFeatureStoreDataStorage format
51+
const deserializedData: LDFeatureStoreDataStorage = {
52+
features: deserialized.flags,
53+
segments: deserialized.segments,
54+
};
55+
this._logger.debug('Successfully cached deserialized data');
56+
57+
return deserializedData;
58+
})();
59+
} else {
60+
this._logger.debug('Using cached deserialized data');
3661
}
37-
return this._kvData;
62+
return this._deserializedPromise;
3863
}
3964

4065
async get(
@@ -47,23 +72,18 @@ export class EdgeFeatureStore implements LDFeatureStore {
4772
this._logger.debug(`Requesting ${dataKey} from ${this._rootKey}.${kindKey}`);
4873

4974
try {
50-
const i = await this._getKVData();
75+
const data = await this._getKVData();
5176

52-
if (!i) {
77+
if (!data) {
5378
throw new Error(`${this._rootKey}.${kindKey} is not found in KV.`);
5479
}
5580

56-
const item = deserializePoll(i);
57-
if (!item) {
58-
throw new Error(`Error deserializing ${kindKey}`);
59-
}
60-
6181
switch (namespace) {
6282
case 'features':
63-
callback(item.flags[dataKey]);
83+
callback(data.features[dataKey]);
6484
break;
6585
case 'segments':
66-
callback(item.segments[dataKey]);
86+
callback(data.segments[dataKey]);
6787
break;
6888
default:
6989
callback(null);
@@ -79,22 +99,17 @@ export class EdgeFeatureStore implements LDFeatureStore {
7999
const kindKey = namespace === 'features' ? 'flags' : namespace;
80100
this._logger.debug(`Requesting all from ${this._rootKey}.${kindKey}`);
81101
try {
82-
const i = await this._getKVData();
83-
if (!i) {
102+
const data = await this._getKVData();
103+
if (!data) {
84104
throw new Error(`${this._rootKey}.${kindKey} is not found in KV.`);
85105
}
86106

87-
const item = deserializePoll(i);
88-
if (!item) {
89-
throw new Error(`Error deserializing ${kindKey}`);
90-
}
91-
92107
switch (namespace) {
93108
case 'features':
94-
callback(item.flags);
109+
callback(data.features);
95110
break;
96111
case 'segments':
97-
callback(item.segments);
112+
callback(data.segments);
98113
break;
99114
default:
100115
callback({});
@@ -106,13 +121,22 @@ export class EdgeFeatureStore implements LDFeatureStore {
106121
}
107122

108123
async initialized(callback: (isInitialized: boolean) => void = noop): Promise<void> {
109-
const config = await this._getKVData();
110-
const result = config !== null;
111-
this._logger.debug(`Is ${this._rootKey} initialized? ${result}`);
112-
callback(result);
124+
try {
125+
const deserialized = await this._getKVData();
126+
const result = deserialized !== null;
127+
this._logger.debug(`Is ${this._rootKey} initialized? ${result}`);
128+
callback(result);
129+
} catch (err) {
130+
this._logger.error(err);
131+
this._logger.debug(`Is ${this._rootKey} initialized? false`);
132+
callback(false);
133+
}
113134
}
114135

115136
init(allData: LDFeatureStoreDataStorage, callback: () => void): void {
137+
this._getKVData().catch((err) => {
138+
this._logger.error(err);
139+
});
116140
callback();
117141
}
118142

0 commit comments

Comments
 (0)