Loki language provider: don't cache empty array while querying (#92092)

* Loki language provider: don't cache empty array while querying

* Prettier

* Unfocus test

* chore: add extra assertion
This commit is contained in:
Matias Chomicki
2024-08-21 13:07:01 +00:00
committed by GitHub
parent 4bc64dd747
commit 1b6db3da22
2 changed files with 45 additions and 10 deletions
@@ -268,6 +268,24 @@ describe('Language completion provider', () => {
end: expect.any(Number),
});
});
it('should use a single promise to resolve values', async () => {
const datasource = setup({ testkey: ['label1_val1', 'label1_val2'], label2: [] });
const provider = await getLanguageProvider(datasource);
const requestSpy = jest.spyOn(provider, 'request');
const promise1 = provider.fetchLabelValues('testkey');
const promise2 = provider.fetchLabelValues('testkey');
const promise3 = provider.fetchLabelValues('testkeyNOPE');
expect(requestSpy).toHaveBeenCalledTimes(2);
const values1 = await promise1;
const values2 = await promise2;
const values3 = await promise3;
expect(values1).toStrictEqual(values2);
expect(values2).not.toStrictEqual(values3);
expect(requestSpy).toHaveBeenCalledTimes(2);
});
});
describe('fetchLabels', () => {
@@ -31,6 +31,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
*/
private seriesCache = new LRUCache<string, Record<string, string[]>>({ max: 10 });
private labelsCache = new LRUCache<string, string[]>({ max: 10 });
private labelsPromisesCache = new LRUCache<string, Promise<string[]>>({ max: 10 });
constructor(datasource: LokiDatasource, initialValues?: any) {
super();
@@ -272,18 +273,34 @@ export default class LokiLanguageProvider extends LanguageProvider {
const cacheKey = this.generateCacheKey(url, start, end, paramCacheKey);
let labelValues = this.labelsCache.get(cacheKey);
if (!labelValues) {
// Clear value when requesting new one. Empty object being truthy also makes sure we don't request twice.
this.labelsCache.set(cacheKey, []);
const res = await this.request(url, params);
if (Array.isArray(res)) {
labelValues = res.slice().sort();
this.labelsCache.set(cacheKey, labelValues);
}
// Values in cache, return
const labelValues = this.labelsCache.get(cacheKey);
if (labelValues) {
return labelValues;
}
return labelValues ?? [];
// Promise in cache, return
let labelValuesPromise = this.labelsPromisesCache.get(cacheKey);
if (labelValuesPromise) {
return labelValuesPromise;
}
labelValuesPromise = new Promise(async (resolve) => {
try {
const data = await this.request(url, params);
if (Array.isArray(data)) {
const labelValues = data.slice().sort();
this.labelsCache.set(cacheKey, labelValues);
this.labelsPromisesCache.delete(cacheKey);
resolve(labelValues);
}
} catch (error) {
console.error(error);
resolve([]);
}
});
this.labelsPromisesCache.set(cacheKey, labelValuesPromise);
return labelValuesPromise;
}
/**