Repository: grafana/grafana
Pull request #101889: Prometheus: Use timeRange parameter in each language provider method
Author: login: itsmylife
Labels: name: datasource/Prometheus
name: area/frontend
name: no-backport
description: Skip backport of PR
name: levitate breaking change
description: A label indicating a breaking change and assigned by Levitate.
name: no-changelog
description: Skip including change in changelog/release notes
Created: 2025-03-10T15:59:10Z | Merged: 2025-03-21T09:08:23Z

**What is this feature?**

`PromQlLanguageProvider` and `PrometheusMetricFindQuery` methods relies on `timeRange` that we have. But currently we create them with timeRange and update the time range when it's changed. That is working nicely for most of the cases. But it has some downsides. Not always we use the up-to-date time range and that causes the results outside of our selected time range. In order to fix that this PR adds `timeRange` parameter to all methods. 

With this PR we ensures that everytime we use a languageProvider method we pass the `timeRange` parameter. So existing functionality is still working as expected. On top of that, if we have a template variable that refreshes "on time range change" will be fetched with the correct time range parameters.

**Why do we need this feature?**

Better reflecting on time range changes. 

**Who is this feature for?**

All prometheus users

**Which issue(s) does this PR fix?**:

Fixes https://github.com/grafana/grafana/issues/101793

**Special notes for your reviewer:**

Please check that:
- [ ] It works as expected from a user's perspective.
- [ ] If this is a pre-GA feature, it is behind a feature toggle.
- [ ] The docs are updated, and if this is a [notable improvement](https://grafana.com/docs/writers-toolkit/contribute/release-notes/#how-to-determine-if-content-belongs-in-whats-new), it's added to our [What's New](https://grafana.com/docs/writers-toolkit/contribute/release-notes/) doc.



Diff:
diff --git a/packages/grafana-prometheus/src/components/PromQueryField.tsx b/packages/grafana-prometheus/src/components/PromQueryField.tsx
index 4c619f88b7a54..05cdda9864c84 100644
--- a/packages/grafana-prometheus/src/components/PromQueryField.tsx
+++ b/packages/grafana-prometheus/src/components/PromQueryField.tsx
@@ -3,6 +3,7 @@ import { css, cx } from '@emotion/css';
 import { PureComponent, ReactNode } from 'react';
 
 import {
+  getDefaultTimeRange,
   isDataFrame,
   LocalStorageValueProvider,
   QueryEditorProps,
@@ -249,6 +250,7 @@ class PromQueryFieldClass extends PureComponent<PromQueryFieldProps, PromQueryFi
                     initialValue={query.expr ?? ''}
                     placeholder="Enter a PromQL query…"
                     datasource={datasource}
+                    timeRange={this.props.range ?? getDefaultTimeRange()}
                   />
                 </div>
               </div>
diff --git a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx
index df14c9e02c846..602aeca8f6a9d 100644
--- a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx
+++ b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx
@@ -2,7 +2,7 @@
 import { render, screen, waitFor } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 
-import { createTheme } from '@grafana/data';
+import { createTheme, getDefaultTimeRange, TimeRange } from '@grafana/data';
 
 import PromQlLanguageProvider from '../language_provider';
 
@@ -146,7 +146,7 @@ describe('PrometheusMetricsBrowser', () => {
   const setupProps = (): BrowserProps => {
     const mockLanguageProvider = {
       start: () => Promise.resolve(),
-      getLabelValues: (name: string) => {
+      getLabelValues: (timeRange: TimeRange, name: string) => {
         switch (name) {
           case 'label1':
             return ['value1-1', 'value1-2'];
@@ -163,7 +163,7 @@ describe('PrometheusMetricsBrowser', () => {
       // The metrics browser expects both label names and label values.
       // The labels endpoint with match does not supply label values
       // and so using it breaks the metrics browser.
-      fetchSeriesLabels: (selector: string) => {
+      fetchSeriesLabels: (timeRange: TimeRange, selector: string) => {
         switch (selector) {
           case '{label1="value1-1"}':
             return { label1: ['value1-1'], label2: ['value2-1'], label3: ['value3-1'] };
@@ -187,6 +187,7 @@ describe('PrometheusMetricsBrowser', () => {
       lastUsedLabels: [],
       storeLastUsedLabels: () => {},
       deleteLastUsedLabels: () => {},
+      timeRange: getDefaultTimeRange(),
     };
 
     return defaults;
diff --git a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx
index 1eeb59d35ed59..db3ec6c60b46f 100644
--- a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx
+++ b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx
@@ -4,7 +4,7 @@ import { ChangeEvent } from 'react';
 import * as React from 'react';
 import { FixedSizeList } from 'react-window';
 
-import { GrafanaTheme2, TimeRange } from '@grafana/data';
+import { getDefaultTimeRange, GrafanaTheme2, TimeRange } from '@grafana/data';
 import { selectors } from '@grafana/e2e-selectors';
 import {
   BrowserLabel as PromLabel,
@@ -409,11 +409,15 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
     }
   };
 
+  getTimeRange = (): TimeRange => {
+    return this.props.timeRange ?? getDefaultTimeRange();
+  };
+
   async fetchValues(name: string, selector: string) {
     const { languageProvider } = this.props;
     this.updateLabelState(name, { loading: true }, `Fetching values for ${name}`);
     try {
-      let rawValues = await languageProvider.getLabelValues(name);
+      let rawValues = await languageProvider.getLabelValues(this.getTimeRange(), name);
       // If selector changed, clear loading state and discard result by returning early
       if (selector !== buildSelector(this.state.labels)) {
         this.updateLabelState(name, { loading: false });
@@ -444,7 +448,12 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
       this.updateLabelState(lastFacetted, { loading: true }, `Facetting labels for ${selector}`);
     }
     try {
-      const possibleLabels = await languageProvider.fetchSeriesLabels(selector, true, this.state.seriesLimit);
+      const possibleLabels = await languageProvider.fetchSeriesLabels(
+        this.getTimeRange(),
+        selector,
+        true,
+        this.state.seriesLimit
+      );
       // If selector changed, clear loading state and discard result by returning early
       if (selector !== buildSelector(this.state.labels)) {
         if (lastFacetted) {
@@ -469,7 +478,7 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
   async validateSelector(selector: string) {
     const { languageProvider } = this.props;
     this.setState({ validationStatus: `Validating selector ${selector}`, error: '' });
-    const streams = await languageProvider.fetchSeries(selector);
+    const streams = await languageProvider.fetchSeries(this.getTimeRange(), selector);
     this.setState({ validationStatus: `Selector is valid (${streams.length} series found)` });
   }
 
diff --git a/packages/grafana-prometheus/src/components/VariableQueryEditor.tsx b/packages/grafana-prometheus/src/components/VariableQueryEditor.tsx
index ab7f041590c7e..97b6116153600 100644
--- a/packages/grafana-prometheus/src/components/VariableQueryEditor.tsx
+++ b/packages/grafana-prometheus/src/components/VariableQueryEditor.tsx
@@ -2,7 +2,7 @@
 import debounce from 'debounce-promise';
 import { FormEvent, useCallback, useEffect, useState } from 'react';
 
-import { QueryEditorProps, SelectableValue, toOption } from '@grafana/data';
+import { getDefaultTimeRange, QueryEditorProps, SelectableValue, toOption } from '@grafana/data';
 import { selectors } from '@grafana/e2e-selectors';
 import { AsyncSelect, InlineField, InlineFieldRow, Input, Select, TextArea } from '@grafana/ui';
 
@@ -111,9 +111,14 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
       return;
     }
     const variables = datasource.getVariables().map((variable: string) => ({ label: variable, value: variable }));
+    let timeRange = range;
+    if (!timeRange) {
+      timeRange = getDefaultTimeRange();
+    }
+
     if (!metric) {
       // get all the labels
-      datasource.getTagKeys({ filters: [] }).then((labelNames: Array<{ text: string }>) => {
+      datasource.getTagKeys({ timeRange, filters: [] }).then((labelNames: Array<{ text: string }>) => {
         const names = labelNames.map(({ text }) => ({ label: text, value: text }));
         setLabels(names, variables);
       });
@@ -122,13 +127,15 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
       const labelToConsider = [{ label: '__name__', op: '=', value: metric }];
       const expr = promQueryModeller.renderLabels(labelToConsider);
 
-      datasource.languageProvider.fetchLabelsWithMatch(expr).then((labelsIndex: Record<string, string[]>) => {
-        const labelNames = Object.keys(labelsIndex);
-        const names = labelNames.map((value) => ({ label: value, value: value }));
-        setLabels(names, variables);
-      });
+      datasource.languageProvider
+        .fetchLabelsWithMatch(timeRange, expr)
+        .then((labelsIndex: Record<string, string[]>) => {
+          const labelNames = Object.keys(labelsIndex);
+          const names = labelNames.map((value) => ({ label: value, value: value }));
+          setLabels(names, variables);
+        });
     }
-  }, [datasource, qryType, metric]);
+  }, [datasource, qryType, metric, range]);
 
   const onChangeWithVariableString = (
     updateVar: { [key: string]: QueryType | string },
@@ -302,6 +309,7 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
             datasource={datasource}
             onChange={metricsLabelsChange}
             variableEditor={true}
+            timeRange={range ?? getDefaultTimeRange()}
           />
         </>
       )}
diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx
index 97e49df416b3d..d6c86499c25d9 100644
--- a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx
+++ b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx
@@ -105,7 +105,7 @@ const MonacoQueryField = (props: Props) => {
   // we need only one instance of `overrideServices` during the lifetime of the react component
   const overrideServicesRef = useRef(getOverrideServices());
   const containerRef = useRef<HTMLDivElement>(null);
-  const { languageProvider, history, onBlur, onRunQuery, initialValue, placeholder, datasource } = props;
+  const { languageProvider, history, onBlur, onRunQuery, initialValue, placeholder, datasource, timeRange } = props;
 
   const lpRef = useLatest(languageProvider);
   const historyRef = useLatest(history);
@@ -155,7 +155,7 @@ const MonacoQueryField = (props: Props) => {
             historyProvider: historyRef.current,
             languageProvider: lpRef.current,
           });
-          const completionProvider = getCompletionProvider(monaco, dataProvider);
+          const completionProvider = getCompletionProvider(monaco, dataProvider, timeRange);
 
           // completion-providers in monaco are not registered directly to editor-instances,
           // they are registered to languages. this makes it hard for us to have
diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts
index 4ca3fd894ed2c..ac2995273e522 100644
--- a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts
+++ b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts
@@ -1,5 +1,5 @@
 // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryFieldProps.ts
-import { HistoryItem } from '@grafana/data';
+import { HistoryItem, TimeRange } from '@grafana/data';
 
 import { PrometheusDatasource } from '../../datasource';
 import type PromQlLanguageProvider from '../../language_provider';
@@ -17,4 +17,5 @@ export type Props = {
   onRunQuery: (value: string) => void;
   onBlur: (value: string) => void;
   datasource: PrometheusDatasource;
+  timeRange: TimeRange;
 };
diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts
index 7a97ba987400c..a778cf7430028 100644
--- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts
+++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts
@@ -2,6 +2,7 @@ import { config } from '@grafana/runtime';
 
 import { SUGGESTIONS_LIMIT } from '../../../language_provider';
 import { FUNCTIONS } from '../../../promql';
+import { getMockTimeRange } from '../../../test/__mocks__/datasource';
 
 import { filterMetricNames, getCompletions } from './completions';
 import { DataProvider, type DataProviderParams } from './data_provider';
@@ -183,6 +184,8 @@ function getSuggestionCountForSituation(situationType: MetricNameSituation, metr
 }
 
 describe.each(metricNameCompletionSituations)('metric name completions in situation %s', (situationType) => {
+  const timeRange = getMockTimeRange();
+
   it('should return completions for all metric names when the number of metric names is at or below the limit', async () => {
     jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValue(metrics.atLimit);
     const expectedCompletionsCount = getSuggestionCountForSituation(situationType, metrics.atLimit.length);
@@ -192,12 +195,12 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat
 
     // No text input
     dataProvider.monacoSettings.setInputInRange('');
-    let completions = await getCompletions(situation, dataProvider);
+    let completions = await getCompletions(situation, dataProvider, timeRange);
     expect(completions).toHaveLength(expectedCompletionsCount);
 
     // With text input (use fuzzy search)
     dataProvider.monacoSettings.setInputInRange('name_1');
-    completions = await getCompletions(situation, dataProvider);
+    completions = await getCompletions(situation, dataProvider, timeRange);
     expect(completions?.length).toBeLessThanOrEqual(expectedCompletionsCount);
   });
 
@@ -210,12 +213,12 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat
 
     // Complex query
     dataProvider.monacoSettings.setInputInRange('metric name one two three four five');
-    let completions = await getCompletions(situation, dataProvider);
+    let completions = await getCompletions(situation, dataProvider, timeRange);
     expect(completions.length).toBeLessThanOrEqual(expectedCompletionsCount);
 
     // Simple query with fuzzy match
     dataProvider.monacoSettings.setInputInRange('metric_name_');
-    completions = await getCompletions(situation, dataProvider);
+    completions = await getCompletions(situation, dataProvider, timeRange);
     expect(completions.length).toBeLessThanOrEqual(expectedCompletionsCount);
   });
 
@@ -227,19 +230,19 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat
     // Do not cross the metrics names threshold
     jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValueOnce(metrics.atLimit);
     dataProvider.monacoSettings.setInputInRange('name_1');
-    await getCompletions(situation, dataProvider);
+    await getCompletions(situation, dataProvider, timeRange);
     expect(dataProvider.monacoSettings.suggestionsIncomplete).toBe(false);
 
     // Cross the metric names threshold, without text input
     jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValueOnce(metrics.beyondLimit);
     dataProvider.monacoSettings.setInputInRange('');
-    await getCompletions(situation, dataProvider);
+    await getCompletions(situation, dataProvider, timeRange);
     expect(dataProvider.monacoSettings.suggestionsIncomplete).toBe(true);
 
     // Cross the metric names threshold, with text input
     jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValueOnce(metrics.beyondLimit);
     dataProvider.monacoSettings.setInputInRange('name_1');
-    await getCompletions(situation, dataProvider);
+    await getCompletions(situation, dataProvider, timeRange);
     expect(dataProvider.monacoSettings.suggestionsIncomplete).toBe(true);
   });
 
@@ -253,7 +256,7 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat
 
     // Test with a complex query (> 4 terms)
     dataProvider.monacoSettings.setInputInRange('metric name 1 with extra terms more');
-    const completions = await getCompletions(situation, dataProvider);
+    const completions = await getCompletions(situation, dataProvider, timeRange);
 
     const metricCompletions = completions.filter((c) => c.type === 'METRIC_NAME');
     expect(metricCompletions.some((c) => c.label === 'metric_name_1_with_extra_terms')).toBe(true);
@@ -268,7 +271,7 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat
 
     // Test with multiple terms
     dataProvider.monacoSettings.setInputInRange('metric name 1 2 3 4 5');
-    const completions = await getCompletions(situation, dataProvider);
+    const completions = await getCompletions(situation, dataProvider, timeRange);
 
     const expectedCompletionsCount = getSuggestionCountForSituation(situationType, metrics.beyondLimit.length);
     expect(completions.length).toBeLessThanOrEqual(expectedCompletionsCount);
@@ -308,6 +311,8 @@ describe('Label value completions', () => {
       });
     });
 
+    const timeRange = getMockTimeRange();
+
     it('should not escape special characters when between quotes', async () => {
       const situation: Situation = {
         type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
@@ -316,7 +321,7 @@ describe('Label value completions', () => {
         otherLabels: [],
       };
 
-      const completions = await getCompletions(situation, dataProvider);
+      const completions = await getCompletions(situation, dataProvider, timeRange);
 
       expect(completions).toHaveLength(4);
       expect(completions[0].insertText).toBe('value1');
@@ -333,7 +338,7 @@ describe('Label value completions', () => {
         otherLabels: [],
       };
 
-      const completions = await getCompletions(situation, dataProvider);
+      const completions = await getCompletions(situation, dataProvider, timeRange);
 
       expect(completions).toHaveLength(4);
       expect(completions[0].insertText).toBe('"value1"');
@@ -350,6 +355,8 @@ describe('Label value completions', () => {
       });
     });
 
+    const timeRange = getMockTimeRange();
+
     it('should escape special characters when between quotes', async () => {
       const situation: Situation = {
         type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
@@ -358,7 +365,7 @@ describe('Label value completions', () => {
         otherLabels: [],
       };
 
-      const completions = await getCompletions(situation, dataProvider);
+      const completions = await getCompletions(situation, dataProvider, timeRange);
 
       expect(completions).toHaveLength(4);
       expect(completions[0].insertText).toBe('value1');
@@ -375,7 +382,7 @@ describe('Label value completions', () => {
         otherLabels: [],
       };
 
-      const completions = await getCompletions(situation, dataProvider);
+      const completions = await getCompletions(situation, dataProvider, timeRange);
 
       expect(completions).toHaveLength(4);
       expect(completions[0].insertText).toBe('"value1"');
@@ -392,6 +399,8 @@ describe('Label value completions', () => {
       });
     });
 
+    const timeRange = getMockTimeRange();
+
     it('should handle empty values', async () => {
       jest.spyOn(dataProvider, 'getLabelValues').mockResolvedValue(['']);
 
@@ -402,7 +411,7 @@ describe('Label value completions', () => {
         otherLabels: [],
       };
 
-      const completions = await getCompletions(situation, dataProvider);
+      const completions = await getCompletions(situation, dataProvider, timeRange);
       expect(completions).toHaveLength(1);
       expect(completions[0].insertText).toBe('""');
     });
@@ -417,7 +426,7 @@ describe('Label value completions', () => {
         otherLabels: [],
       };
 
-      const completions = await getCompletions(situation, dataProvider);
+      const completions = await getCompletions(situation, dataProvider, timeRange);
       expect(completions).toHaveLength(1);
       expect(completions[0].insertText).toBe('test\\"\\\\value');
     });
@@ -432,7 +441,7 @@ describe('Label value completions', () => {
         otherLabels: [],
       };
 
-      const completions = await getCompletions(situation, dataProvider);
+      const completions = await getCompletions(situation, dataProvider, timeRange);
       expect(completions).toHaveLength(1);
       expect(completions[0].insertText).toBe('"123"');
     });
diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts
index ff1f049056029..af0b51bddd44b 100644
--- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts
+++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts
@@ -2,6 +2,7 @@
 import UFuzzy from '@leeoniya/ufuzzy';
 import { languages } from 'monaco-editor';
 
+import { TimeRange } from '@grafana/data';
 import { config } from '@grafana/runtime';
 
 import { prometheusRegularEscape } from '../../../datasource';
@@ -160,14 +161,15 @@ function makeSelector(metricName: string | undefined, labels: Label[]): string {
 async function getLabelNames(
   metric: string | undefined,
   otherLabels: Label[],
-  dataProvider: DataProvider
+  dataProvider: DataProvider,
+  timeRange: TimeRange
 ): Promise<string[]> {
   if (metric === undefined && otherLabels.length === 0) {
     // if there is no filtering, we have to use a special endpoint
     return Promise.resolve(dataProvider.getAllLabelNames());
   } else {
     const selector = makeSelector(metric, otherLabels);
-    return await dataProvider.getSeriesLabels(selector, otherLabels);
+    return await dataProvider.getSeriesLabels(timeRange, selector, otherLabels);
   }
 }
 
@@ -176,9 +178,10 @@ async function getLabelNamesForCompletions(
   suffix: string,
   triggerOnInsert: boolean,
   otherLabels: Label[],
-  dataProvider: DataProvider
+  dataProvider: DataProvider,
+  timeRange: TimeRange
 ): Promise<Completion[]> {
-  const labelNames = await getLabelNames(metric, otherLabels, dataProvider);
+  const labelNames = await getLabelNames(metric, otherLabels, dataProvider, timeRange);
   return labelNames.map((text) => {
     const isUtf8 = !isValidLegacyName(text);
     return {
@@ -200,31 +203,34 @@ async function getLabelNamesForCompletions(
 async function getLabelNamesForSelectorCompletions(
   metric: string | undefined,
   otherLabels: Label[],
-  dataProvider: DataProvider
+  dataProvider: DataProvider,
+  timeRange: TimeRange
 ): Promise<Completion[]> {
-  return getLabelNamesForCompletions(metric, '=', true, otherLabels, dataProvider);
+  return getLabelNamesForCompletions(metric, '=', true, otherLabels, dataProvider, timeRange);
 }
 
 async function getLabelNamesForByCompletions(
   metric: string | undefined,
   otherLabels: Label[],
-  dataProvider: DataProvider
+  dataProvider: DataProvider,
+  timeRange: TimeRange
 ): Promise<Completion[]> {
-  return getLabelNamesForCompletions(metric, '', false, otherLabels, dataProvider);
+  return getLabelNamesForCompletions(metric, '', false, otherLabels, dataProvider, timeRange);
 }
 
 async function getLabelValues(
   metric: string | undefined,
   labelName: string,
   otherLabels: Label[],
-  dataProvider: DataProvider
+  dataProvider: DataProvider,
+  timeRange: TimeRange
 ): Promise<string[]> {
   if (metric === undefined && otherLabels.length === 0) {
     // if there is no filtering, we have to use a special endpoint
-    return dataProvider.getLabelValues(labelName);
+    return dataProvider.getLabelValues(timeRange, labelName);
   } else {
     const selector = makeSelector(metric, otherLabels);
-    return await dataProvider.getSeriesValues(labelName, selector);
+    return await dataProvider.getSeriesValues(timeRange, labelName, selector);
   }
 }
 
@@ -233,9 +239,10 @@ async function getLabelValuesForMetricCompletions(
   labelName: string,
   betweenQuotes: boolean,
   otherLabels: Label[],
-  dataProvider: DataProvider
+  dataProvider: DataProvider,
+  timeRange: TimeRange
 ): Promise<Completion[]> {
-  const values = await getLabelValues(metric, labelName, otherLabels, dataProvider);
+  const values = await getLabelValues(metric, labelName, otherLabels, dataProvider, timeRange);
   return values.map((text) => ({
     type: 'LABEL_VALUE',
     label: text,
@@ -248,7 +255,11 @@ function formatLabelValueForCompletion(value: string, betweenQuotes: boolean): s
   return betweenQuotes ? text : `"${text}"`;
 }
 
-export function getCompletions(situation: Situation, dataProvider: DataProvider): Promise<Completion[]> {
+export function getCompletions(
+  situation: Situation,
+  dataProvider: DataProvider,
+  timeRange: TimeRange
+): Promise<Completion[]> {
   switch (situation.type) {
     case 'IN_DURATION':
       return Promise.resolve(DURATION_COMPLETIONS);
@@ -263,16 +274,17 @@ export function getCompletions(situation: Situation, dataProvider: DataProvider)
       return Promise.resolve([...historyCompletions, ...FUNCTION_COMPLETIONS, ...metricNames]);
     }
     case 'IN_LABEL_SELECTOR_NO_LABEL_NAME':
-      return getLabelNamesForSelectorCompletions(situation.metricName, situation.otherLabels, dataProvider);
+      return getLabelNamesForSelectorCompletions(situation.metricName, situation.otherLabels, dataProvider, timeRange);
     case 'IN_GROUPING':
-      return getLabelNamesForByCompletions(situation.metricName, situation.otherLabels, dataProvider);
+      return getLabelNamesForByCompletions(situation.metricName, situation.otherLabels, dataProvider, timeRange);
     case 'IN_LABEL_SELECTOR_WITH_LABEL_NAME':
       return getLabelValuesForMetricCompletions(
         situation.metricName,
         situation.labelName,
         situation.betweenQuotes,
         situation.otherLabels,
-        dataProvider
+        dataProvider,
+        timeRange
       );
     default:
       throw new NeverCaseError(situation);
diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/index.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/index.ts
index 5108401b2fdd6..def57a02621fe 100644
--- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/index.ts
+++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/index.ts
@@ -1,4 +1,5 @@
 // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/index.ts
+import { TimeRange } from '@grafana/data';
 import type { Monaco, monacoTypes } from '@grafana/ui';
 
 import { CompletionType, getCompletions } from './completions';
@@ -48,7 +49,8 @@ function getMonacoCompletionItemKind(type: CompletionType, monaco: Monaco): mona
 
 export function getCompletionProvider(
   monaco: Monaco,
-  dataProvider: DataProvider
+  dataProvider: DataProvider,
+  timeRange: TimeRange
 ): monacoTypes.languages.CompletionItemProvider {
   const provideCompletionItems = (
     model: monacoTypes.editor.ITextModel,
@@ -84,7 +86,8 @@ export function getCompletionProvider(
 
     const offset = model.getOffsetAt(positionClone);
     const situation = getSituation(model.getValue(), offset);
-    const completionsPromise = situation != null ? getCompletions(situation, dataProvider) : Promise.resolve([]);
+    const completionsPromise =
+      situation != null ? getCompletions(situation, dataProvider, timeRange) : Promise.resolve([]);
 
     return completionsPromise.then((items) => {
       // monaco by-default alphabetically orders the items.
diff --git a/packages/grafana-prometheus/src/datasource.ts b/packages/grafana-prometheus/src/datasource.ts
index 44e93ebb7d1be..082e391ad9b6d 100644
--- a/packages/grafana-prometheus/src/datasource.ts
+++ b/packages/grafana-prometheus/src/datasource.ts
@@ -483,7 +483,7 @@ export class PrometheusDatasource
       return Promise.resolve([]);
     }
 
-    const timeRange = options?.range ?? this.languageProvider.timeRange ?? getDefaultTimeRange();
+    const timeRange = options?.range ?? getDefaultTimeRange();
 
     const scopedVars = {
       ...this.getIntervalVars(),
@@ -654,6 +654,10 @@ export class PrometheusDatasource
   // it is used in metric_find_query.ts
   // and in Tempo here grafana/public/app/plugins/datasource/tempo/QueryEditor/ServiceGraphSection.tsx
   async getTagKeys(options: DataSourceGetTagKeysOptions<PromQuery>): Promise<MetricFindValue[]> {
+    if (!options.timeRange) {
+      options.timeRange = getDefaultTimeRange();
+    }
+
     if (config.featureToggles.promQLScope && (options?.scopes?.length ?? 0) > 0) {
       const suggestions = await this.languageProvider.fetchSuggestions(
         options.timeRange,
@@ -680,7 +684,10 @@ export class PrometheusDatasource
     }));
     const expr = promQueryModeller.renderLabels(labelFilters);
 
-    let labelsIndex: Record<string, string[]> = await this.languageProvider.fetchLabelsWithMatch(expr);
+    let labelsIndex: Record<string, string[]> = await this.languageProvider.fetchLabelsWithMatch(
+      options.timeRange,
+      expr
+    );
 
     // filter out already used labels
     return Object.keys(labelsIndex)
@@ -689,7 +696,11 @@ export class PrometheusDatasource
   }
 
   // By implementing getTagKeys and getTagValues we add ad-hoc filters functionality
-  async getTagValues(options: DataSourceGetTagValuesOptions<PromQuery>) {
+  async getTagValues(options: DataSourceGetTagValuesOptions<PromQuery>): Promise<MetricFindValue[]> {
+    if (!options.timeRange) {
+      options.timeRange = getDefaultTimeRange();
+    }
+
     const requestId = `[${this.uid}][${options.key}]`;
     if (config.featureToggles.promQLScope && (options?.scopes?.length ?? 0) > 0) {
       return (
@@ -715,7 +726,7 @@ export class PrometheusDatasource
 
     if (this.hasLabelsMatchAPISupport()) {
       return (
-        await this.languageProvider.fetchSeriesValuesWithMatch(options.key, expr, requestId, options.timeRange)
+        await this.languageProvider.fetchSeriesValuesWithMatch(options.timeRange, options.key, expr, requestId)
       ).map((v) => ({
         value: v,
         text: v,
diff --git a/packages/grafana-prometheus/src/language_provider.test.ts b/packages/grafana-prometheus/src/language_provider.test.ts
index 2b0c7edcaf3e5..e91845d1f001f 100644
--- a/packages/grafana-prometheus/src/language_provider.test.ts
+++ b/packages/grafana-prometheus/src/language_provider.test.ts
@@ -110,6 +110,8 @@ describe('Language completion provider', () => {
   });
 
   describe('getSeriesLabels', () => {
+    const timeRange = getMockTimeRange();
+
     it('should call labels endpoint', () => {
       const languageProvider = new LanguageProvider({
         ...defaultDatasource,
@@ -120,7 +122,7 @@ describe('Language completion provider', () => {
 
       const labelName = 'job';
       const labelValue = 'grafana';
-      getSeriesLabels(`{${labelName}="${labelValue}"}`, [
+      getSeriesLabels(timeRange, `{${labelName}="${labelValue}"}`, [
         {
           name: labelName,
           value: labelValue,
@@ -151,7 +153,7 @@ describe('Language completion provider', () => {
 
       const labelName = 'job';
       const labelValue = 'grafana';
-      getSeriesLabels(`{${labelName}="${labelValue}"}`, [
+      getSeriesLabels(timeRange, `{${labelName}="${labelValue}"}`, [
         {
           name: labelName,
           value: labelValue,
@@ -186,7 +188,7 @@ describe('Language completion provider', () => {
 
       const labelName = 'job';
       const labelValue = 'grafana';
-      getSeriesLabels(`{${labelName}="${labelValue}"}`, [
+      getSeriesLabels(timeRange, `{${labelName}="${labelValue}"}`, [
         {
           name: labelName,
           value: labelValue,
@@ -217,13 +219,15 @@ describe('Language completion provider', () => {
   });
 
   describe('getSeriesValues', () => {
+    const timeRange = getMockTimeRange();
+
     it('should call old series endpoint and should use match[] parameter', () => {
       const languageProvider = new LanguageProvider({
         ...defaultDatasource,
       } as PrometheusDatasource);
       const getSeriesValues = languageProvider.getSeriesValues;
       const requestSpy = jest.spyOn(languageProvider, 'request');
-      getSeriesValues('job', '{job="grafana"}');
+      getSeriesValues(timeRange, 'job', '{job="grafana"}');
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         '/api/v1/series',
@@ -246,7 +250,7 @@ describe('Language completion provider', () => {
       const requestSpy = jest.spyOn(languageProvider, 'request');
       const labelName = 'job';
       const labelValue = 'grafana';
-      getSeriesValues(labelName, `{${labelName}="${labelValue}"}`);
+      getSeriesValues(timeRange, labelName, `{${labelName}="${labelValue}"}`);
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         `/api/v1/label/${labelName}/values`,
@@ -267,7 +271,7 @@ describe('Language completion provider', () => {
       } as PrometheusDatasource);
       const getSeriesValues = languageProvider.getSeriesValues;
       const requestSpy = jest.spyOn(languageProvider, 'request');
-      getSeriesValues('job', '{instance="$instance", job="grafana"}');
+      getSeriesValues(timeRange, 'job', '{instance="$instance", job="grafana"}');
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         '/api/v1/series',
@@ -288,7 +292,7 @@ describe('Language completion provider', () => {
       const timeRange = getMockTimeRange();
       await languageProvider.start(timeRange);
       const requestSpy = jest.spyOn(languageProvider, 'request');
-      await languageProvider.fetchSeries('{job="grafana"}');
+      await languageProvider.fetchSeries(timeRange, '{job="grafana"}');
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         '/api/v1/series',
@@ -311,7 +315,7 @@ describe('Language completion provider', () => {
       } as PrometheusDatasource);
       const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
       const requestSpy = jest.spyOn(languageProvider, 'request');
-      fetchSeriesLabels('$metric');
+      fetchSeriesLabels(getMockTimeRange(), '$metric');
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         '/api/v1/series',
@@ -332,7 +336,7 @@ describe('Language completion provider', () => {
       } as PrometheusDatasource);
       const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
       const requestSpy = jest.spyOn(languageProvider, 'request');
-      fetchSeriesLabels('metric-with-limit', undefined, 'none');
+      fetchSeriesLabels(getMockTimeRange(), 'metric-with-limit', undefined, 'none');
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         '/api/v1/series',
@@ -353,7 +357,7 @@ describe('Language completion provider', () => {
       } as PrometheusDatasource);
       const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
       const requestSpy = jest.spyOn(languageProvider, 'request');
-      fetchSeriesLabels('metric-without-limit', false, 'none');
+      fetchSeriesLabels(getMockTimeRange(), 'metric-without-limit', false, 'none');
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         '/api/v1/series',
@@ -619,7 +623,7 @@ describe('Language completion provider', () => {
       } as PrometheusDatasource);
       const fetchLabelValues = languageProvider.fetchLabelValues;
       const requestSpy = jest.spyOn(languageProvider, 'request');
-      fetchLabelValues('$job');
+      fetchLabelValues(getMockTimeRange(), '$job');
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         '/api/v1/label/interpolated_job/values',
@@ -639,7 +643,7 @@ describe('Language completion provider', () => {
       } as PrometheusDatasource);
       const fetchLabelValues = languageProvider.fetchLabelValues;
       const requestSpy = jest.spyOn(languageProvider, 'request');
-      fetchLabelValues('"http.status:sum"');
+      fetchLabelValues(getMockTimeRange(), '"http.status:sum"');
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         '/api/v1/label/U__http_2e_status:sum/values',
@@ -661,7 +665,7 @@ describe('Language completion provider', () => {
       } as PrometheusDatasource);
       const fetchSeriesValuesWithMatch = languageProvider.fetchSeriesValuesWithMatch;
       const requestSpy = jest.spyOn(languageProvider, 'request');
-      fetchSeriesValuesWithMatch('"http.status:sum"', '{__name__="a_utf8_http_requests_total"}');
+      fetchSeriesValuesWithMatch(getMockTimeRange(), '"http.status:sum"', '{__name__="a_utf8_http_requests_total"}');
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         '/api/v1/label/U__http_2e_status:sum/values',
@@ -681,7 +685,7 @@ describe('Language completion provider', () => {
       } as PrometheusDatasource);
       const fetchSeriesValuesWithMatch = languageProvider.fetchSeriesValuesWithMatch;
       const requestSpy = jest.spyOn(languageProvider, 'request');
-      fetchSeriesValuesWithMatch('"http_status_sum"', '{__name__="a_utf8_http_requests_total"}');
+      fetchSeriesValuesWithMatch(getMockTimeRange(), '"http_status_sum"', '{__name__="a_utf8_http_requests_total"}');
       expect(requestSpy).toHaveBeenCalled();
       expect(requestSpy).toHaveBeenCalledWith(
         '/api/v1/label/http_status_sum/values',
diff --git a/packages/grafana-prometheus/src/language_provider.ts b/packages/grafana-prometheus/src/language_provider.ts
index 22c427c979bf3..04a9f7e6e7baf 100644
--- a/packages/grafana-prometheus/src/language_provider.ts
+++ b/packages/grafana-prometheus/src/language_provider.ts
@@ -79,7 +79,6 @@ const PREFIX_DELIMITER_REGEX =
 const secondsInDay = 86400;
 export default class PromQlLanguageProvider extends LanguageProvider {
   histogramMetrics: string[];
-  timeRange: TimeRange;
   metrics: string[];
   metricsMetadata?: PromMetricsMetadata;
   declare startTask: Promise<any>;
@@ -92,7 +91,6 @@ export default class PromQlLanguageProvider extends LanguageProvider {
 
     this.datasource = datasource;
     this.histogramMetrics = [];
-    this.timeRange = getDefaultTimeRange();
     this.metrics = [];
 
     Object.assign(this, initialValues);
@@ -109,7 +107,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
   cleanText(s: string) {
     const parts = s.split(PREFIX_DELIMITER_REGEX);
     const last = parts.pop()!;
-    return last.trimLeft().replace(/"$/, '').replace(/^"/, '');
+    return last.trimStart().replace(/"$/, '').replace(/^"/, '');
   }
 
   get syntax() {
@@ -129,16 +127,14 @@ export default class PromQlLanguageProvider extends LanguageProvider {
     return defaultValue;
   };
 
-  start = async (timeRange?: TimeRange): Promise<any[]> => {
-    this.timeRange = timeRange ?? getDefaultTimeRange();
-
+  start = async (timeRange: TimeRange = getDefaultTimeRange()): Promise<any[]> => {
     if (this.datasource.lookupsDisabled) {
       return [];
     }
 
-    this.metrics = (await this.fetchLabelValues('__name__')) || [];
+    this.metrics = (await this.fetchLabelValues(timeRange, '__name__')) || [];
     this.histogramMetrics = processHistogramMetrics(this.metrics).sort();
-    return Promise.all([this.loadMetricsMetadata(), this.fetchLabels()]);
+    return Promise.all([this.loadMetricsMetadata(), this.fetchLabels(timeRange)]);
   };
 
   async loadMetricsMetadata() {
@@ -186,15 +182,15 @@ export default class PromQlLanguageProvider extends LanguageProvider {
     };
   }
 
-  async getSeries(selector: string, withName?: boolean): Promise<Record<string, string[]>> {
+  async getSeries(timeRange: TimeRange, selector: string, withName?: boolean): Promise<Record<string, string[]>> {
     if (this.datasource.lookupsDisabled) {
       return {};
     }
     try {
       if (selector === EMPTY_SELECTOR) {
-        return await this.fetchDefaultSeries();
+        return await this.fetchDefaultSeries(timeRange);
       } else {
-        return await this.fetchSeriesLabels(selector, withName, REMOVE_SERIES_LIMIT);
+        return await this.fetchSeriesLabels(timeRange, selector, withName, REMOVE_SERIES_LIMIT);
       }
     } catch (error) {
       // TODO: better error handling
@@ -203,11 +199,8 @@ export default class PromQlLanguageProvider extends LanguageProvider {
     }
   }
 
-  /**
-   * @param key
-   */
-  fetchLabelValues = async (key: string): Promise<string[]> => {
-    const params = this.datasource.getAdjustedInterval(this.timeRange);
+  fetchLabelValues = async (range: TimeRange, key: string): Promise<string[]> => {
+    const params = this.datasource.getAdjustedInterval(range);
     const interpolatedName = this.datasource.interpolateString(key);
     const interpolatedAndEscapedName = escapeForUtf8Support(removeQuotesIfExist(interpolatedName));
     const url = `/api/v1/label/${interpolatedAndEscapedName}/values`;
@@ -215,19 +208,16 @@ export default class PromQlLanguageProvider extends LanguageProvider {
     return value ?? [];
   };
 
-  async getLabelValues(key: string): Promise<string[]> {
-    return await this.fetchLabelValues(key);
+  async getLabelValues(range: TimeRange, key: string): Promise<string[]> {
+    return await this.fetchLabelValues(range, key);
   }
 
   /**
    * Fetches all label keys
    */
-  fetchLabels = async (timeRange?: TimeRange, queries?: PromQuery[]): Promise<string[]> => {
-    if (timeRange) {
-      this.timeRange = timeRange;
-    }
+  fetchLabels = async (timeRange: TimeRange, queries?: PromQuery[]): Promise<string[]> => {
     let url = '/api/v1/labels';
-    const timeParams = this.datasource.getAdjustedInterval(this.timeRange);
+    const timeParams = this.datasource.getAdjustedInterval(timeRange);
     this.labelFetchTs = Date.now().valueOf();
 
     const searchParams = new URLSearchParams({ ...timeParams });
@@ -259,17 +249,15 @@ export default class PromQlLanguageProvider extends LanguageProvider {
 
   /**
    * Gets series values
-   * Function to replace old getSeries calls in a way that will provide faster endpoints for new prometheus instances,
-   * while maintaining backward compatability
-   * @param labelName
-   * @param selector
+   * Function to replace old getSeries calls in a way that will provide faster endpoints
+   * for new prometheus instances, while maintaining backward compatability
    */
-  getSeriesValues = async (labelName: string, selector: string): Promise<string[]> => {
+  getSeriesValues = async (timeRange: TimeRange, labelName: string, selector: string): Promise<string[]> => {
     if (!this.datasource.hasLabelsMatchAPISupport()) {
-      const data = await this.getSeries(selector);
+      const data = await this.getSeries(timeRange, selector);
       return data[removeQuotesIfExist(labelName)] ?? [];
     }
-    return await this.fetchSeriesValuesWithMatch(labelName, selector);
+    return await this.fetchSeriesValuesWithMatch(timeRange, labelName, selector);
   };
 
   /**
@@ -280,10 +268,10 @@ export default class PromQlLanguageProvider extends LanguageProvider {
    * @param requestId
    */
   fetchSeriesValuesWithMatch = async (
+    timeRange: TimeRange,
     name: string,
     match: string,
-    requestId?: string,
-    timeRange: TimeRange = this.timeRange
+    requestId?: string
   ): Promise<string[]> => {
     const interpolatedName = name ? this.datasource.interpolateString(name) : null;
     const interpolatedMatch = match ? this.datasource.interpolateString(match) : null;
@@ -321,16 +309,16 @@ export default class PromQlLanguageProvider extends LanguageProvider {
    * @param selector
    * @param otherLabels
    */
-  getSeriesLabels = async (selector: string, otherLabels: Label[]): Promise<string[]> => {
+  getSeriesLabels = async (timeRange: TimeRange, selector: string, otherLabels: Label[]): Promise<string[]> => {
     let possibleLabelNames, data: Record<string, string[]>;
 
     if (!this.datasource.hasLabelsMatchAPISupport()) {
-      data = await this.getSeries(selector);
+      data = await this.getSeries(timeRange, selector);
       possibleLabelNames = Object.keys(data); // all names from prometheus
     } else {
       // Exclude __name__ from output
       otherLabels.push({ name: '__name__', value: '', op: '!=' });
-      data = await this.fetchSeriesLabelsMatch(selector);
+      data = await this.fetchSeriesLabelsMatch(timeRange, selector);
       possibleLabelNames = Object.keys(data);
     }
 
@@ -341,31 +329,31 @@ export default class PromQlLanguageProvider extends LanguageProvider {
   /**
    * Fetch labels using the best endpoint that datasource supports.
    * This is cached by its args but also by the global timeRange currently selected as they can change over requested time.
-   * @param name
-   * @param withName
    */
-  fetchLabelsWithMatch = async (name: string, withName?: boolean): Promise<Record<string, string[]>> => {
+  fetchLabelsWithMatch = async (
+    timeRange: TimeRange,
+    name: string,
+    withName?: boolean
+  ): Promise<Record<string, string[]>> => {
     if (this.datasource.hasLabelsMatchAPISupport()) {
-      return this.fetchSeriesLabelsMatch(name, withName);
+      return this.fetchSeriesLabelsMatch(timeRange, name, withName);
     } else {
-      return this.fetchSeriesLabels(name, withName, REMOVE_SERIES_LIMIT);
+      return this.fetchSeriesLabels(timeRange, name, withName, REMOVE_SERIES_LIMIT);
     }
   };
 
   /**
    * Fetch labels for a series using /series endpoint. This is cached by its args but also by the global timeRange currently selected as
    * they can change over requested time.
-   * @param name
-   * @param withName
-   * @param withLimit
    */
   fetchSeriesLabels = async (
+    timeRange: TimeRange,
     name: string,
     withName?: boolean,
     withLimit?: string
   ): Promise<Record<string, string[]>> => {
     const interpolatedName = this.datasource.interpolateString(name);
-    const range = this.datasource.getAdjustedInterval(this.timeRange);
+    const range = this.datasource.getAdjustedInterval(timeRange);
     let urlParams: UrlParamsType = {
       ...range,
       'match[]': interpolatedName,
@@ -385,12 +373,14 @@ export default class PromQlLanguageProvider extends LanguageProvider {
   /**
    * Fetch labels for a series using /labels endpoint.  This is cached by its args but also by the global timeRange currently selected as
    * they can change over requested time.
-   * @param name
-   * @param withName
    */
-  fetchSeriesLabelsMatch = async (name: string, withName?: boolean): Promise<Record<string, string[]>> => {
+  fetchSeriesLabelsMatch = async (
+    timeRange: TimeRange,
+    name: string,
+    withName?: boolean
+  ): Promise<Record<string, string[]>> => {
     const interpolatedName = this.datasource.interpolateString(name);
-    const range = this.datasource.getAdjustedInterval(this.timeRange);
+    const range = this.datasource.getAdjustedInterval(timeRange);
     const urlParams = {
       ...range,
       'match[]': interpolatedName,
@@ -404,11 +394,10 @@ export default class PromQlLanguageProvider extends LanguageProvider {
 
   /**
    * Fetch series for a selector. Use this for raw results. Use fetchSeriesLabels() to get labels.
-   * @param match
    */
-  fetchSeries = async (match: string): Promise<Array<Record<string, string>>> => {
+  fetchSeries = async (timeRange: TimeRange, match: string): Promise<Array<Record<string, string>>> => {
     const url = '/api/v1/series';
-    const range = this.datasource.getTimeRangeParams(this.timeRange);
+    const range = this.datasource.getTimeRangeParams(timeRange);
     const params = { ...range, 'match[]': match };
     return await this.request(url, {}, params, this.getDefaultCacheHeaders());
   };
@@ -418,8 +407,8 @@ export default class PromQlLanguageProvider extends LanguageProvider {
    * because we can cache more aggressively here and also we do not want to invalidate this cache the same way as in
    * fetchSeriesLabels.
    */
-  fetchDefaultSeries = once(async () => {
-    const values = await Promise.all(DEFAULT_KEYS.map((key) => this.fetchLabelValues(key)));
+  fetchDefaultSeries = once(async (timeRange: TimeRange) => {
+    const values = await Promise.all(DEFAULT_KEYS.map((key) => this.fetchLabelValues(timeRange, key)));
     return DEFAULT_KEYS.reduce((acc, key, i) => ({ ...acc, [key]: values[i] }), {});
   });
 
@@ -442,12 +431,12 @@ export default class PromQlLanguageProvider extends LanguageProvider {
     limit?: number,
     requestId?: string
   ): Promise<string[]> => {
-    if (timeRange) {
-      this.timeRange = timeRange;
+    if (!timeRange) {
+      timeRange = getDefaultTimeRange();
     }
 
     const url = '/suggestions';
-    const timeParams = this.datasource.getAdjustedInterval(this.timeRange);
+    const timeParams = this.datasource.getAdjustedInterval(timeRange);
     const value = await this.request(
       url,
       [],
@@ -456,7 +445,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
         queries: queries?.map((q) =>
           this.datasource.interpolateString(q.expr, {
             ...this.datasource.getIntervalVars(),
-            ...this.datasource.getRangeScopedVars(this.timeRange),
+            ...this.datasource.getRangeScopedVars(timeRange),
           })
         ),
         scopes: scopes?.reduce<ScopeSpecFilter[]>((acc, scope) => {
diff --git a/packages/grafana-prometheus/src/metric_find_query.ts b/packages/grafana-prometheus/src/metric_find_query.ts
index 69494f0f7fc59..12d17e7126ff9 100644
--- a/packages/grafana-prometheus/src/metric_find_query.ts
+++ b/packages/grafana-prometheus/src/metric_find_query.ts
@@ -1,7 +1,7 @@
 // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/metric_find_query.ts
 import { chain, map as _map, uniq } from 'lodash';
 
-import { getDefaultTimeRange, MetricFindValue, TimeRange } from '@grafana/data';
+import { MetricFindValue, TimeRange } from '@grafana/data';
 
 import { PrometheusDatasource } from './datasource';
 import { getPrometheusTime } from './language_utils';
@@ -15,19 +15,15 @@ import {
 import { escapeForUtf8Support, isValidLegacyName } from './utf8_support';
 
 export class PrometheusMetricFindQuery {
-  range: TimeRange;
-
   constructor(
     private datasource: PrometheusDatasource,
     private query: string
   ) {
     this.datasource = datasource;
     this.query = query;
-    this.range = getDefaultTimeRange();
   }
 
   process(timeRange: TimeRange): Promise<MetricFindValue[]> {
-    this.range = timeRange;
     const labelNamesRegex = PrometheusLabelNamesRegex;
     const labelNamesRegexWithMatch = PrometheusLabelNamesRegexWithMatch;
     const labelValuesRegex = PrometheusLabelValuesRegex;
@@ -38,7 +34,7 @@ export class PrometheusMetricFindQuery {
 
     if (labelNamesMatchQuery) {
       const selector = `{__name__=~".*${labelNamesMatchQuery[1]}.*"}`;
-      return this.datasource.languageProvider.getSeriesLabels(selector, []).then((results) =>
+      return this.datasource.languageProvider.getSeriesLabels(timeRange, selector, []).then((results) =>
         results.map((result) => ({
           text: result,
         }))
@@ -54,35 +50,35 @@ export class PrometheusMetricFindQuery {
       const filter = labelValuesQuery[1];
       const label = labelValuesQuery[2];
       if (isFilterDefined(filter)) {
-        return this.labelValuesQuery(label, filter);
+        return this.labelValuesQuery(label, timeRange, filter);
       } else {
         // Exclude the filter part of the expression because it is blank or empty
-        return this.labelValuesQuery(label);
+        return this.labelValuesQuery(label, timeRange);
       }
     }
 
     const metricNamesQuery = this.query.match(metricNamesRegex);
     if (metricNamesQuery) {
-      return this.metricNameQuery(metricNamesQuery[1]);
+      return this.metricNameQuery(metricNamesQuery[1], timeRange);
     }
 
     const queryResultQuery = this.query.match(queryResultRegex);
     if (queryResultQuery) {
-      return this.queryResultQuery(queryResultQuery[1]);
+      return this.queryResultQuery(queryResultQuery[1], timeRange);
     }
 
     // if query contains full metric name, return metric name and label list
     const expressions = ['label_values()', 'metrics()', 'query_result()'];
     if (!expressions.includes(this.query)) {
-      return this.metricNameAndLabelsQuery(this.query);
+      return this.metricNameAndLabelsQuery(this.query, timeRange);
     }
 
     return Promise.resolve([]);
   }
 
-  labelValuesQuery(label: string, metric?: string) {
-    const start = getPrometheusTime(this.range.from, false);
-    const end = getPrometheusTime(this.range.to, true);
+  labelValuesQuery(label: string, range: TimeRange, metric?: string) {
+    const start = getPrometheusTime(range.from, false);
+    const end = getPrometheusTime(range.to, true);
     const params = { ...(metric && { 'match[]': metric }), start: start.toString(), end: end.toString() };
 
     let escapedLabel = label;
@@ -118,9 +114,9 @@ export class PrometheusMetricFindQuery {
     }
   }
 
-  metricNameQuery(metricFilterPattern: string) {
-    const start = getPrometheusTime(this.range.from, false);
-    const end = getPrometheusTime(this.range.to, true);
+  metricNameQuery(metricFilterPattern: string, range: TimeRange) {
+    const start = getPrometheusTime(range.from, false);
+    const end = getPrometheusTime(range.to, true);
     const params = {
       start: start.toString(),
       end: end.toString(),
@@ -143,11 +139,11 @@ export class PrometheusMetricFindQuery {
     });
   }
 
-  queryResultQuery(query: string) {
+  queryResultQuery(query: string, range: TimeRange) {
     const url = '/api/v1/query';
     const params = {
       query,
-      time: getPrometheusTime(this.range.to, true).toString(),
+      time: getPrometheusTime(range.to, true).toString(),
     };
     return this.datasource.metadataRequest(url, params).then((result) => {
       switch (result.data.data.resultType) {
@@ -182,9 +178,9 @@ export class PrometheusMetricFindQuery {
     });
   }
 
-  metricNameAndLabelsQuery(query: string): Promise<MetricFindValue[]> {
-    const start = getPrometheusTime(this.range.from, false);
-    const end = getPrometheusTime(this.range.to, true);
+  metricNameAndLabelsQuery(query: string, range: TimeRange): Promise<MetricFindValue[]> {
+    const start = getPrometheusTime(range.from, false);
+    const end = getPrometheusTime(range.to, true);
     const params = {
       'match[]': query,
       start: start.toString(),
diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx
index ae6353e8b1a9c..f6f62ffb54b6e 100644
--- a/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx
+++ b/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx
@@ -1,7 +1,7 @@
 // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/MetricsLabelsSection.tsx
 import { useCallback } from 'react';
 
-import { SelectableValue } from '@grafana/data';
+import { SelectableValue, TimeRange } from '@grafana/data';
 import { config } from '@grafana/runtime';
 
 import { PrometheusDatasource } from '../../datasource';
@@ -22,6 +22,7 @@ export interface MetricsLabelsSectionProps {
   onChange: (update: PromVisualQuery) => void;
   variableEditor?: boolean;
   onBlur?: () => void;
+  timeRange: TimeRange;
 }
 
 export function MetricsLabelsSection({
@@ -30,6 +31,7 @@ export function MetricsLabelsSection({
   onChange,
   onBlur,
   variableEditor,
+  timeRange,
 }: MetricsLabelsSectionProps) {
   // fixing the use of 'as' from refactoring
   // @ts-ignore
@@ -63,7 +65,7 @@ export function MetricsLabelsSection({
   const onGetLabelNames = async (forLabel: Partial<QueryBuilderLabelFilter>): Promise<SelectableValue[]> => {
     // If no metric we need to use a different method
     if (!query.metric) {
-      await datasource.languageProvider.fetchLabels();
+      await datasource.languageProvider.fetchLabels(timeRange);
       return datasource.languageProvider.getLabelKeys().map((k) => ({ value: k }));
     }
 
@@ -71,7 +73,7 @@ export function MetricsLabelsSection({
     labelsToConsider.push({ label: '__name__', op: '=', value: query.metric });
     const expr = promQueryModeller.renderLabels(labelsToConsider);
 
-    let labelsIndex: Record<string, string[]> = await datasource.languageProvider.fetchLabelsWithMatch(expr);
+    let labelsIndex: Record<string, string[]> = await datasource.languageProvider.fetchLabelsWithMatch(timeRange, expr);
 
     // filter out already used labels
     return Object.keys(labelsIndex)
@@ -124,7 +126,7 @@ export function MetricsLabelsSection({
     if (!forLabel.label) {
       return Promise.resolve([]);
     }
-    const result = datasource.languageProvider.fetchSeries(promQLExpression);
+    const result = datasource.languageProvider.fetchSeries(timeRange, promQLExpression);
     const forLabelInterpolated = datasource.interpolateString(forLabel.label);
     return result.then((result) => {
       // This query returns duplicate values, scrub them out
@@ -154,7 +156,7 @@ export function MetricsLabelsSection({
     const requestId = `[${datasource.uid}][${query.metric}][${forLabel.label}][${forLabel.op}]`;
 
     return datasource.languageProvider
-      .fetchSeriesValuesWithMatch(forLabel.label, promQLExpression, requestId)
+      .fetchSeriesValuesWithMatch(timeRange, forLabel.label, promQLExpression, requestId)
       .then((response) => response.map((v) => ({ value: v, label: v })));
   };
 
@@ -169,7 +171,7 @@ export function MetricsLabelsSection({
     }
     // If no metric is selected, we can get the raw list of labels
     if (!query.metric) {
-      return (await datasource.languageProvider.getLabelValues(forLabel.label)).map((v) => ({ value: v }));
+      return (await datasource.languageProvider.getLabelValues(timeRange, forLabel.label)).map((v) => ({ value: v }));
     }
 
     const labelsToConsider = query.labels.filter((x) => x !== forLabel);
@@ -191,8 +193,8 @@ export function MetricsLabelsSection({
   };
 
   const onGetMetrics = useCallback(() => {
-    return withTemplateVariableOptions(getMetrics(datasource, query));
-  }, [datasource, query, withTemplateVariableOptions]);
+    return withTemplateVariableOptions(getMetrics(datasource, query, timeRange));
+  }, [datasource, query, timeRange, withTemplateVariableOptions]);
 
   const MetricSelectComponent = config.featureToggles.prometheusUsesCombobox ? MetricCombobox : MetricSelect;
 
@@ -229,7 +231,8 @@ export function MetricsLabelsSection({
  */
 async function getMetrics(
   datasource: PrometheusDatasource,
-  query: PromVisualQuery
+  query: PromVisualQuery,
+  timeRange: TimeRange
 ): Promise<Array<{ value: string; description?: string }>> {
   // Makes sure we loaded the metadata for metrics. Usually this is done in the start() method of the provider but we
   // don't use it with the visual builder and there is no need to run all the start() setup anyway.
@@ -245,9 +248,9 @@ async function getMetrics(
   let metrics: string[];
   if (query.labels.length > 0) {
     const expr = promQueryModeller.renderLabels(query.labels);
-    metrics = (await datasource.languageProvider.getSeries(expr, true))['__name__'] ?? [];
+    metrics = (await datasource.languageProvider.getSeries(timeRange, expr, true))['__name__'] ?? [];
   } else {
-    metrics = (await datasource.languageProvider.getLabelValues('__name__')) ?? [];
+    metrics = (await datasource.languageProvider.getLabelValues(timeRange, '__name__')) ?? [];
   }
 
   return metrics.map((m) => ({
diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx
index 8e58f4959630d..372873eebb141 100644
--- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx
+++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx
@@ -89,7 +89,7 @@ describe('PromQueryBuilder', () => {
   it('tries to load metrics without labels', async () => {
     const { languageProvider, container } = setup();
     await openMetricSelect(container);
-    await waitFor(() => expect(languageProvider.getLabelValues).toHaveBeenCalledWith('__name__'));
+    await waitFor(() => expect(languageProvider.getLabelValues).toHaveBeenCalledWith(expect.anything(), '__name__'));
   });
 
   it('tries to load metrics with labels', async () => {
@@ -98,7 +98,13 @@ describe('PromQueryBuilder', () => {
       labels: [{ label: 'label_name', op: '=', value: 'label_value' }],
     });
     await openMetricSelect(container);
-    await waitFor(() => expect(languageProvider.getSeries).toHaveBeenCalledWith('{label_name="label_value"}', true));
+    await waitFor(() =>
+      expect(languageProvider.getSeries).toHaveBeenCalledWith(
+        expect.anything(),
+        '{label_name="label_value"}',
+        expect.anything()
+      )
+    );
   });
 
   it('tries to load variables in metric field', async () => {
@@ -113,7 +119,10 @@ describe('PromQueryBuilder', () => {
     const { languageProvider } = setup();
     await openLabelNameSelect();
     await waitFor(() =>
-      expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith('{__name__="random_metric"}')
+      expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith(
+        expect.anything(),
+        '{__name__="random_metric"}'
+      )
     );
   });
 
@@ -135,6 +144,7 @@ describe('PromQueryBuilder', () => {
     await openLabelNameSelect(1);
     await waitFor(() =>
       expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith(
+        expect.anything(),
         '{label_name="label_value", __name__="random_metric"}'
       )
     );
@@ -273,7 +283,10 @@ describe('PromQueryBuilder', () => {
     });
     await openLabelNameSelect();
     await waitFor(() =>
-      expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith('{__name__="random_metric"}')
+      expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith(
+        expect.anything(),
+        '{__name__="random_metric"}'
+      )
     );
   });
 
@@ -301,6 +314,7 @@ describe('PromQueryBuilder', () => {
     await openLabelNameSelect(1);
     await waitFor(() =>
       expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith(
+        expect.anything(),
         '{label_name="label_value", __name__="random_metric"}'
       )
     );
diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx
index 11c79f5932c38..a7e0841f6347f 100644
--- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx
+++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx
@@ -2,7 +2,7 @@
 import { css } from '@emotion/css';
 import { memo, useState } from 'react';
 
-import { DataSourceApi, PanelData } from '@grafana/data';
+import { DataSourceApi, getDefaultTimeRange, PanelData } from '@grafana/data';
 import { selectors } from '@grafana/e2e-selectors';
 import { EditorRow } from '@grafana/plugin-ui';
 
@@ -43,7 +43,12 @@ export const PromQueryBuilder = memo<PromQueryBuilderProps>((props) => {
   return (
     <>
       <EditorRow>
-        <MetricsLabelsSection query={query} onChange={onChange} datasource={datasource} />
+        <MetricsLabelsSection
+          query={query}
+          onChange={onChange}
+          datasource={datasource}
+          timeRange={data?.timeRange ?? getDefaultTimeRange()}
+        />
       </EditorRow>
       {initHints.length ? (
         <div


Reviews:
user: login: itsgareth
body: left a few comments, other than that LGTM
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-19T14:00:28Z
user: login: itsmylife
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T08:41:30Z
user: login: itsmylife
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T08:41:36Z
user: login: bohandley
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T20:22:25Z
user: login: bohandley
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T20:35:35Z
user: login: svennergr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T22:32:49Z

Review comments:
diff_hunk: @@ -15,19 +15,16 @@ import {
 import { escapeForUtf8Support, isValidLegacyName } from './utf8_support';
 
 export class PrometheusMetricFindQuery {
-  range: TimeRange;
-
   constructor(
     private datasource: PrometheusDatasource,
     private query: string
   ) {
     this.datasource = datasource;
     this.query = query;
-    this.range = getDefaultTimeRange();
   }
 
   process(timeRange: TimeRange): Promise<MetricFindValue[]> {
-    this.range = timeRange;
+    // this.range = timeRange;
path: packages/grafana-prometheus/src/metric_find_query.ts
user: login: itsgareth
body: this can be removed right?
created_at: 2025-03-19T13:23:32Z
author_association: MEMBER
diff_hunk: @@ -89,7 +89,7 @@ describe('PromQueryBuilder', () => {
   it('tries to load metrics without labels', async () => {
     const { languageProvider, container } = setup();
     await openMetricSelect(container);
-    await waitFor(() => expect(languageProvider.getLabelValues).toHaveBeenCalledWith('__name__'));
+    await waitFor(() => expect((languageProvider.getLabelValues as jest.Mock).mock.calls[0][1]).toEqual('__name__'));
path: packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx
user: login: itsgareth
body: i think we can use jest [`expect.anything()`](https://jestjs.io/docs/expect#expectanything) here to make this more readable. 

```ts
await waitFor(() => expect(languageProvider.getLabelValues).toHaveBeenCalledWith(expect.anything(), '__name__'));
```
created_at: 2025-03-19T13:57:30Z
author_association: MEMBER
diff_hunk: @@ -98,7 +98,10 @@ describe('PromQueryBuilder', () => {
       labels: [{ label: 'label_name', op: '=', value: 'label_value' }],
     });
     await openMetricSelect(container);
-    await waitFor(() => expect(languageProvider.getSeries).toHaveBeenCalledWith('{label_name="label_value"}', true));
+    await waitFor(() =>
+      expect((languageProvider.getSeries as jest.Mock).mock.calls[0][1]).toEqual('{label_name="label_value"}')
path: packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx
user: login: itsgareth
body: same suggestion as above, the same also applies to all other usages of `mock.calls[0][1]` in this file 😄 
created_at: 2025-03-19T14:00:02Z
author_association: MEMBER
diff_hunk: @@ -98,7 +98,10 @@ describe('PromQueryBuilder', () => {
       labels: [{ label: 'label_name', op: '=', value: 'label_value' }],
     });
     await openMetricSelect(container);
-    await waitFor(() => expect(languageProvider.getSeries).toHaveBeenCalledWith('{label_name="label_value"}', true));
+    await waitFor(() =>
+      expect((languageProvider.getSeries as jest.Mock).mock.calls[0][1]).toEqual('{label_name="label_value"}')
path: packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx
user: login: itsmylife
body: https://github.com/grafana/grafana/pull/101889/commits/f16a50ef7463b94f5aa6f67e949b8fd0e45d9a2b
created_at: 2025-03-21T08:41:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -89,7 +89,7 @@ describe('PromQueryBuilder', () => {
   it('tries to load metrics without labels', async () => {
     const { languageProvider, container } = setup();
     await openMetricSelect(container);
-    await waitFor(() => expect(languageProvider.getLabelValues).toHaveBeenCalledWith('__name__'));
+    await waitFor(() => expect((languageProvider.getLabelValues as jest.Mock).mock.calls[0][1]).toEqual('__name__'));
path: packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx
user: login: itsmylife
body: https://github.com/grafana/grafana/pull/101889/commits/f16a50ef7463b94f5aa6f67e949b8fd0e45d9a2b
created_at: 2025-03-21T08:41:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -409,11 +409,15 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
     }
   };
 
+  getTimeRange = (): TimeRange => {
+    return this.props.timeRange ?? getDefaultTimeRange();
+  };
+
   async fetchValues(name: string, selector: string) {
     const { languageProvider } = this.props;
     this.updateLabelState(name, { loading: true }, `Fetching values for ${name}`);
     try {
-      let rawValues = await languageProvider.getLabelValues(name);
+      let rawValues = await languageProvider.getLabelValues(this.getTimeRange(), name);
path: packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx
user: login: bohandley
body: Hi @itsmylife! Just wanted to point out that we were using this in Grafana Metrics Drilldown and changing the signature caused a bug. We always need to pass in the time range now I see! And handle when GMD is in older versions without this change in function parameters.

I see levitate noted it as a breaking change. Just wanted to point this out!


created_at: 2025-03-24T20:22:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -715,7 +726,7 @@ export class PrometheusDatasource
 
     if (this.hasLabelsMatchAPISupport()) {
       return (
-        await this.languageProvider.fetchSeriesValuesWithMatch(options.key, expr, requestId, options.timeRange)
+        await this.languageProvider.fetchSeriesValuesWithMatch(options.timeRange, options.key, expr, requestId)
path: packages/grafana-prometheus/src/datasource.ts
user: login: bohandley
body: Same here, changing the parameters can cause some downstream issues for any folks who use the library. 🙏🏻 
created_at: 2025-03-24T20:35:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -203,31 +199,25 @@ export default class PromQlLanguageProvider extends LanguageProvider {
     }
   }
 
-  /**
-   * @param key
-   */
-  fetchLabelValues = async (key: string): Promise<string[]> => {
-    const params = this.datasource.getAdjustedInterval(this.timeRange);
+  fetchLabelValues = async (range: TimeRange, key: string): Promise<string[]> => {
path: packages/grafana-prometheus/src/language_provider.ts
user: login: svennergr
body: For posterity: I think it's always easier and "more" safe to introduce new parameters as the last parameter. So even functions just calling `fetchLabelValues("__name__")` would behave normally with this new change.
Maybe just something to consider for future changes.
created_at: 2025-04-04T22:32:48Z
author_association: CONTRIBUTOR

PR comments:
user: login: grafana-pr-automation[bot]
created_at: 2025-03-10T16:27:03Z
body: ⚠️ &nbsp;&nbsp;**Possible breaking changes (md version)**&nbsp;&nbsp; ⚠️

<h3>grafana-prometheus</h3><h4>Removals</h4><b>PromQlLanguageProvider.timeRange</b><br>
<sub>/home/runner/work/grafana/grafana/base/grafana-prometheus/dist/cjs/index.d.cts</sub><br>
<pre lang="typescript">
timeRange: TimeRange;
</pre><br>
<b>PrometheusMetricFindQuery.range</b><br>
<sub>/home/runner/work/grafana/grafana/base/grafana-prometheus/dist/cjs/index.d.cts</sub><br>
<pre lang="typescript">
range: TimeRange;
</pre><br>
<h4>Changes</h4><b>PromQlLanguageProvider.getSeries</b><br>
<sub>/home/runner/work/grafana/grafana/pr/grafana-prometheus/dist/cjs/index.d.cts</sub><br>
<pre lang="diff">
-getSeries(selector: string, withName?: boolean): Promise<Record<string, string[]>>;
+getSeries(timeRange: TimeRange, selector: string, withName?: boolean): Promise<Record<string, string[]>>;

</pre><br>
<b>PromQlLanguageProvider.getLabelValues</b><br>
<sub>/home/runner/work/grafana/grafana/pr/grafana-prometheus/dist/cjs/index.d.cts</sub><br>
<pre lang="diff">
-getLabelValues(key: string): Promise<string[]>;
+getLabelValues(range: TimeRange, key: string): Promise<string[]>;

</pre><br>
<b>PrometheusMetricFindQuery.labelValuesQuery</b><br>
<sub>/home/runner/work/grafana/grafana/pr/grafana-prometheus/dist/cjs/index.d.cts</sub><br>
<pre lang="diff">
-labelValuesQuery(label: string, metric?: string): Promise<{
+labelValuesQuery(label: string, range: TimeRange, metric?: string): Promise<{
         text: any;
     }[]>;

</pre><br>
<b>PrometheusMetricFindQuery.metricNameQuery</b><br>
<sub>/home/runner/work/grafana/grafana/pr/grafana-prometheus/dist/cjs/index.d.cts</sub><br>
<pre lang="diff">
-metricNameQuery(metricFilterPattern: string): Promise<{
+metricNameQuery(metricFilterPattern: string, range: TimeRange): Promise<{
         text: any;
         expandable: boolean;
     }[]>;

</pre><br>
<b>PrometheusMetricFindQuery.queryResultQuery</b><br>
<sub>/home/runner/work/grafana/grafana/pr/grafana-prometheus/dist/cjs/index.d.cts</sub><br>
<pre lang="diff">
-queryResultQuery(query: string): Promise<{
+queryResultQuery(query: string, range: TimeRange): Promise<{
         text: any;
         expandable: boolean;
     }[]>;

</pre><br>
<b>PrometheusMetricFindQuery.metricNameAndLabelsQuery</b><br>
<sub>/home/runner/work/grafana/grafana/pr/grafana-prometheus/dist/cjs/index.d.cts</sub><br>
<pre lang="diff">
-metricNameAndLabelsQuery(query: string): Promise<MetricFindValue[]>;
+metricNameAndLabelsQuery(query: string, range: TimeRange): Promise<MetricFindValue[]>;

</pre><br><br>

[Read our guideline](https://github.com/grafana/grafana/blob/main/contribute/breaking-changes-guide/breaking-changes-guide.md)

* Your pull request merge won't be blocked.
<!-- Sticky Pull Request Commentlevitate-breaking-change-comment -->
author_association: CONTRIBUTOR
user: login: ghost
created_at: 2025-03-10T19:42:32Z
body: ## [Codecov](https://codecov.grafana-dev.net/gh/grafana/grafana/pull/101889?dropdown=coverage&src=pr&el=h1) Report
All modified and coverable lines are covered by tests :white_check_mark:

| [Flag](https://codecov.grafana-dev.net/gh/grafana/grafana/pull/101889/flags?src=pr&el=flags) | Coverage Δ | |
|---|---|---|
| [be-unit](https://codecov.grafana-dev.net/gh/grafana/grafana/pull/101889/flags?src=pr&el=flag) | `40.91% <ø> (+0.17%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=grafana#carryforward-flags-in-the-pull-request-comment) to find out more.

[see 36 files with indirect coverage changes](https://codecov.grafana-dev.net/gh/grafana/grafana/pull/101889/indirect-changes?src=pr&el=tree-more)

author_association: NONE