[v11.2.x] Loki language provider: don't cache empty array while querying (#93931)

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

(cherry picked from commit 1b6db3da22)

Co-authored-by: Matias Chomicki <matyax@gmail.com>
This commit is contained in:
grafana-delivery-bot[bot]
2024-09-27 18:57:35 +02:00
committed by GitHub
co-authored by Matias Chomicki
parent 30a453daa1
commit e763dc131a
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;
}
/**