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]