diff --git a/packages/grafana-data/src/transformations/transformers/histogram.test.ts b/packages/grafana-data/src/transformations/transformers/histogram.test.ts index d7be8281adf..5597eb06787 100644 --- a/packages/grafana-data/src/transformations/transformers/histogram.test.ts +++ b/packages/grafana-data/src/transformations/transformers/histogram.test.ts @@ -896,6 +896,42 @@ describe('getHistogramFields', () => { } `); }); + + it('should prevent excessive densification when sparse histogram has large gaps', () => { + const result = getHistogramFields( + toDataFrame({ + meta: { + type: DataFrameType.HeatmapCells, + }, + fields: [ + { name: 'yMin', type: FieldType.number, values: [0.001, 1000] }, + { name: 'yMax', type: FieldType.number, values: [0.00101, 1010] }, + { name: 'count', type: FieldType.number, values: [10, 20] }, + ], + }) + ); + + expect(result).toBeDefined(); + expect(result!.counts[0].values.length).toBeLessThanOrEqual(1001); + }); + + it('should handle multiple observed buckets when hitting densification limit', () => { + const result = getHistogramFields( + toDataFrame({ + meta: { + type: DataFrameType.HeatmapCells, + }, + fields: [ + { name: 'yMin', type: FieldType.number, values: [0.001, 1000, 2000] }, + { name: 'yMax', type: FieldType.number, values: [0.00101, 1010, 2020] }, + { name: 'count', type: FieldType.number, values: [10, 20, 30] }, + ], + }) + ); + + expect(result).toBeDefined(); + expect(result!.counts[0].values.every((v) => !isNaN(v))).toBe(true); + }); }); describe('joinHistograms', () => { diff --git a/packages/grafana-data/src/transformations/transformers/histogram.ts b/packages/grafana-data/src/transformations/transformers/histogram.ts index 723fb07b0d8..dfd095921d2 100644 --- a/packages/grafana-data/src/transformations/transformers/histogram.ts +++ b/packages/grafana-data/src/transformations/transformers/histogram.ts @@ -210,6 +210,8 @@ export function getHistogramFields(frame: DataFrame): HistogramFields | undefine let denseMins: number[] = []; let denseMaxs: number[] = []; + const MAX_DENSIFIED_BUCKETS = 1000; + for (let i = 0; i < uniqueMaxs.length; i++) { let curMax = uniqueMaxs[i]; let curMin = uniqueMins[i]; @@ -223,13 +225,17 @@ export function getHistogramFields(frame: DataFrame): HistogramFields | undefine curMax = curMax * bucketFactor; curMin = curMin * bucketFactor; - while (curMax < nextMax * 0.999999) { + while (curMax < nextMax * 0.999999 && denseMaxs.length < MAX_DENSIFIED_BUCKETS) { denseMaxs.push(curMax); denseMins.push(curMin); curMax = curMax * bucketFactor; curMin = curMin * bucketFactor; } + + if (denseMaxs.length >= MAX_DENSIFIED_BUCKETS) { + break; + } } } @@ -238,7 +244,10 @@ export function getHistogramFields(frame: DataFrame): HistogramFields | undefine for (let i = 0; i < yMaxField.values.length; i++) { let max = yMaxField.values[i]; - countsByMax.set(max, countsByMax.get(max) + countField.values[i]); + let currentCount = countsByMax.get(max); + if (currentCount !== undefined) { + countsByMax.set(max, currentCount + countField.values[i]); + } } let fields = {