This is an automated email from the ASF dual-hosted git repository.

vogievetsky pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new c3359603431 Web console: Fix explore with far future data (#18721)
c3359603431 is described below

commit c33596034310042b4351d82c38f0a32458e6cd55
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Thu Nov 6 21:59:26 2025 +0000

    Web console: Fix explore with far future data (#18721)
    
    * fix if unable to parse maxTime
    
    * be more defensive about measures being empty
---
 .../time-chart-module/continuous-chart-render.tsx  | 22 ++++++++++++++++----
 .../explore-view/query-macros/max-data-time.ts     | 24 +++++++++++++++++++---
 .../views/explore-view/utils/max-time-for-table.ts | 10 ++++++---
 3 files changed, 46 insertions(+), 10 deletions(-)

diff --git 
a/web-console/src/views/explore-view/modules/time-chart-module/continuous-chart-render.tsx
 
b/web-console/src/views/explore-view/modules/time-chart-module/continuous-chart-render.tsx
index 637a941296d..df8674eae38 100644
--- 
a/web-console/src/views/explore-view/modules/time-chart-module/continuous-chart-render.tsx
+++ 
b/web-console/src/views/explore-view/modules/time-chart-module/continuous-chart-render.tsx
@@ -255,10 +255,13 @@ export const ContinuousChartRender = function 
ContinuousChartRender(
   const chartMargin = { ...margin, ...getDefaultChartMargin(yAxisPosition) };
 
   // Calculate height for each chart based on number of measures
-  const totalGapHeight = (numMeasures - 1) * MEASURE_GAP;
-  const chartHeight = Math.floor(
-    (stage.height - chartMargin.top - chartMargin.bottom - totalGapHeight) / 
numMeasures,
-  );
+  const totalGapHeight = Math.max(0, (numMeasures - 1) * MEASURE_GAP);
+  const chartHeight =
+    numMeasures > 0
+      ? Math.floor(
+          (stage.height - chartMargin.top - chartMargin.bottom - 
totalGapHeight) / numMeasures,
+        )
+      : 0;
   const singleChartStage = new Stage(stage.width, chartHeight);
   const innerStage = singleChartStage.applyMargin({
     top: 0,
@@ -294,6 +297,7 @@ export const ContinuousChartRender = function 
ContinuousChartRender(
   );
 
   function getMeasureIndexFromY(y: number): number {
+    if (numMeasures === 0) return 0;
     const totalChartsHeight = chartHeight * numMeasures + totalGapHeight;
     if (y < 0 || y > totalChartsHeight) return 0;
     const index = Math.floor(y / (chartHeight + MEASURE_GAP));
@@ -522,6 +526,16 @@ export const ContinuousChartRender = function 
ContinuousChartRender(
   const totalChartsHeight = chartHeight * numMeasures + totalGapHeight;
   const xAxisHeight = 25;
 
+  if (numMeasures === 0) {
+    return (
+      <div className="continuous-chart-render">
+        <div className="empty-placeholder">
+          <div className="no-data-text">No measures configured</div>
+        </div>
+      </div>
+    );
+  }
+
   return (
     <div className="continuous-chart-render">
       <svg
diff --git a/web-console/src/views/explore-view/query-macros/max-data-time.ts 
b/web-console/src/views/explore-view/query-macros/max-data-time.ts
index 48a337c41ca..1f0a2627a9b 100644
--- a/web-console/src/views/explore-view/query-macros/max-data-time.ts
+++ b/web-console/src/views/explore-view/query-macros/max-data-time.ts
@@ -16,21 +16,39 @@
  * limitations under the License.
  */
 
+import { Intent } from '@blueprintjs/core';
+import { IconNames } from '@blueprintjs/icons';
 import type { SqlQuery } from 'druid-query-toolkit';
 import { L, SqlFunction } from 'druid-query-toolkit';
 
+import { AppToaster } from '../../../singletons';
 import { getMaxTimeForTable } from '../utils';
 
+const tablesForWhichWeCouldNotDetermineMaxTime = new Set<string>();
+
 export async function rewriteMaxDataTime(
   query: SqlQuery,
 ): Promise<{ query: SqlQuery; maxTime?: Date }> {
   if (!query.containsFunction('MAX_DATA_TIME')) return { query };
 
   const tableName = query.getFirstTableName();
-  if (!tableName) return { query };
+  if (!tableName) throw new Error(`something went wrong - unable to find table 
name in query`);
 
-  const maxTime = await getMaxTimeForTable(tableName);
-  if (!maxTime) return { query };
+  let maxTime: Date;
+  try {
+    maxTime = await getMaxTimeForTable(tableName);
+  } catch (error) {
+    if (!tablesForWhichWeCouldNotDetermineMaxTime.has(tableName)) {
+      tablesForWhichWeCouldNotDetermineMaxTime.add(tableName);
+      AppToaster.show({
+        icon: IconNames.ERROR,
+        intent: Intent.DANGER,
+        timeout: 120000,
+        message: `Could not determine max data time for ${tableName}: 
${error.message}. Using current time instead.`,
+      });
+    }
+    maxTime = new Date();
+  }
 
   const adjustedMaxTime = new Date(maxTime.valueOf() + 1); // Add 1ms to the 
maxTime date to allow filters like `"__time" < {maxTime}" to capture the last 
event which might also be the only event
 
diff --git a/web-console/src/views/explore-view/utils/max-time-for-table.ts 
b/web-console/src/views/explore-view/utils/max-time-for-table.ts
index 8ec4e6d16bb..ab3d57f0bf0 100644
--- a/web-console/src/views/explore-view/utils/max-time-for-table.ts
+++ b/web-console/src/views/explore-view/utils/max-time-for-table.ts
@@ -18,6 +18,7 @@
 
 // micro-cache
 import { sql, T } from 'druid-query-toolkit';
+import * as JSONBig from 'json-bigint-native';
 
 import { deepGet, queryDruidSql } from '../../../utils';
 
@@ -26,7 +27,7 @@ let lastMaxTimeTable: string | undefined;
 let lastMaxTimeValue: Date | undefined;
 let lastMaxTimeTimestamp = 0;
 
-export async function getMaxTimeForTable(tableName: string): Promise<Date | 
undefined> {
+export async function getMaxTimeForTable(tableName: string): Promise<Date> {
   // micro-cache get
   if (
     lastMaxTimeTable === tableName &&
@@ -40,8 +41,11 @@ export async function getMaxTimeForTable(tableName: string): 
Promise<Date | unde
     query: sql`SELECT MAX(__time) AS "maxTime" FROM ${T(tableName)}`,
   });
 
-  const maxTime = new Date(deepGet(d, '0.maxTime'));
-  if (isNaN(maxTime.valueOf())) return;
+  const maxTimeRaw = deepGet(d, '0.maxTime');
+  const maxTime = new Date(maxTimeRaw);
+  if (isNaN(maxTime.valueOf())) {
+    throw new Error(`invalid max data time returned: 
${JSONBig.stringify(maxTimeRaw)}`);
+  }
 
   // micro-cache set
   lastMaxTimeTable = tableName;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to