This is an automated email from the ASF dual-hosted git repository. vogievetsky pushed a commit to branch better_supervisor_view in repository https://gitbox.apache.org/repos/asf/druid.git
commit 06ef03402b6636fd06389be89f25c24dab2fb2aa Author: Vadim Ogievetsky <[email protected]> AuthorDate: Mon Apr 22 15:24:37 2024 -0700 add recent errors --- .../src/utils/local-storage-backed-visibility.tsx | 4 +- .../views/datasources-view/datasources-view.tsx | 5 +- .../src/views/segments-view/segments-view.tsx | 2 +- .../views/supervisors-view/supervisors-view.scss | 4 ++ .../views/supervisors-view/supervisors-view.tsx | 68 ++++++++++++++++++---- 5 files changed, 66 insertions(+), 17 deletions(-) diff --git a/web-console/src/utils/local-storage-backed-visibility.tsx b/web-console/src/utils/local-storage-backed-visibility.tsx index c335180056b..f20031f2b8d 100644 --- a/web-console/src/utils/local-storage-backed-visibility.tsx +++ b/web-console/src/utils/local-storage-backed-visibility.tsx @@ -65,7 +65,7 @@ export class LocalStorageBackedVisibility { return new LocalStorageBackedVisibility(this.key, defaultHidden, newVisibility); } - public shown(value: string): boolean { - return this.visibility[value] ?? !this.defaultHidden.includes(value); + public shown(...values: string[]): boolean { + return values.some(value => this.visibility[value] ?? !this.defaultHidden.includes(value)); } } diff --git a/web-console/src/views/datasources-view/datasources-view.tsx b/web-console/src/views/datasources-view/datasources-view.tsx index 75541b82999..997db1d91af 100644 --- a/web-console/src/views/datasources-view/datasources-view.tsx +++ b/web-console/src/views/datasources-view/datasources-view.tsx @@ -338,12 +338,11 @@ export class DatasourcesView extends React.PureComponent< const columns = compact( [ visibleColumns.shown('Datasource name') && `datasource`, - (visibleColumns.shown('Availability') || visibleColumns.shown('Segment granularity')) && [ + visibleColumns.shown('Availability', 'Segment granularity') && [ `COUNT(*) FILTER (WHERE is_active = 1) AS num_segments`, `COUNT(*) FILTER (WHERE is_published = 1 AND is_overshadowed = 0 AND replication_factor = 0) AS num_zero_replica_segments`, ], - (visibleColumns.shown('Availability') || - visibleColumns.shown('Historical load/drop queues')) && [ + visibleColumns.shown('Availability', 'Historical load/drop queues') && [ `COUNT(*) FILTER (WHERE is_published = 1 AND is_overshadowed = 0 AND is_available = 0 AND replication_factor > 0) AS num_segments_to_load`, `COUNT(*) FILTER (WHERE is_available = 1 AND is_active = 0) AS num_segments_to_drop`, ], diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx index ba4b84b76c4..0e3fe1c6f4b 100644 --- a/web-console/src/views/segments-view/segments-view.tsx +++ b/web-console/src/views/segments-view/segments-view.tsx @@ -201,7 +201,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment WHEN "start" LIKE '%:00.000Z' AND "end" LIKE '%:00.000Z' THEN 'Minute' ELSE 'Sub minute' END AS "time_span"`, - (visibleColumns.shown('Shard type') || visibleColumns.shown('Shard spec')) && `"shard_spec"`, + visibleColumns.shown('Shard type', 'Shard spec') && `"shard_spec"`, visibleColumns.shown('Partition') && `"partition_num"`, visibleColumns.shown('Size') && `"size"`, visibleColumns.shown('Num rows') && `"num_rows"`, diff --git a/web-console/src/views/supervisors-view/supervisors-view.scss b/web-console/src/views/supervisors-view/supervisors-view.scss index 7b03df4f186..25c55e69b44 100644 --- a/web-console/src/views/supervisors-view/supervisors-view.scss +++ b/web-console/src/views/supervisors-view/supervisors-view.scss @@ -37,5 +37,9 @@ font-style: italic; opacity: 0.6; } + + .warning-line { + color: $orange4; + } } } diff --git a/web-console/src/views/supervisors-view/supervisors-view.tsx b/web-console/src/views/supervisors-view/supervisors-view.tsx index 6622cc99e11..316080641ce 100644 --- a/web-console/src/views/supervisors-view/supervisors-view.tsx +++ b/web-console/src/views/supervisors-view/supervisors-view.tsx @@ -92,6 +92,7 @@ const supervisorTableColumns: string[] = [ 'Configured tasks', 'Running tasks', 'Aggregate lag', + 'Recent errors', 'Stats', ACTION_COLUMN_LABEL, ]; @@ -102,6 +103,10 @@ function getRowStatsKeyTitle(key: RowStatsKey) { return `Rate over past ${pluralIfNeeded(parseInt(key, 10), 'minute')}`; } +function getRowStatsKeySeconds(key: RowStatsKey): number { + return parseInt(key, 10) * 60; +} + interface SupervisorQuery extends TableState { capabilities: Capabilities; visibleColumns: LocalStorageBackedVisibility; @@ -288,7 +293,7 @@ export class SupervisorsView extends React.PureComponent< } if (capabilities.hasOverlordAccess()) { - if (visibleColumns.shown('Running tasks') || visibleColumns.shown('Aggregate lag')) { + if (visibleColumns.shown('Running tasks', 'Aggregate lag', 'Recent errors')) { try { for (const supervisor of supervisors) { cancelToken.throwIfRequested(); @@ -674,7 +679,7 @@ export class SupervisorsView extends React.PureComponent< { Header: 'Status', id: 'detailed_state', - width: 150, + width: 130, accessor: 'detailed_state', Cell: ({ value }) => ( <TableFilterableCell @@ -758,7 +763,7 @@ export class SupervisorsView extends React.PureComponent< }, { Header: 'Aggregate lag', - accessor: 'status.aggregateLag', + accessor: 'status.payload.aggregateLag', width: 200, filterable: false, sortable: false, @@ -800,23 +805,64 @@ export class SupervisorsView extends React.PureComponent< Cell: ({ value }) => { if (!value) return; const c = getTotalSupervisorStats(value, statsKey); - const isRate = statsKey !== 'totals'; - const formatNumber = isRate ? formatRate : formatInteger; - const formatData = isRate ? formatByteRate : formatBytes; - const bytes = c.processedBytes ? ` (${formatData(c.processedBytes)})` : ''; + const seconds = getRowStatsKeySeconds(statsKey); + const totalLabel = `Total over ${statsKey}: `; + const bytes = c.processedBytes ? ` (${formatByteRate(c.processedBytes)})` : ''; return ( <div> - <div>{`Processed: ${formatNumber(c.processed)}${bytes}`}</div> + <div + title={`${totalLabel}${formatInteger(c.processed * seconds)} (${formatBytes( + c.processedBytes * seconds, + )})`} + >{`Processed: ${formatRate(c.processed)}${bytes}`}</div> {Boolean(c.processedWithError) && ( - <div>Processed with error: {formatNumber(c.processedWithError)}</div> + <div + className="warning-line" + title={`${totalLabel}${formatInteger(c.processedWithError * seconds)}`} + > + Processed with error: {formatRate(c.processedWithError)} + </div> + )} + {Boolean(c.thrownAway) && ( + <div + className="warning-line" + title={`${totalLabel}${formatInteger(c.thrownAway * seconds)}`} + > + Thrown away: {formatRate(c.thrownAway)} + </div> + )} + {Boolean(c.unparseable) && ( + <div + className="warning-line" + title={`${totalLabel}${formatInteger(c.unparseable * seconds)}`} + > + Unparseable: {formatRate(c.unparseable)} + </div> )} - {Boolean(c.thrownAway) && <div>Thrown away: {formatNumber(c.thrownAway)}</div>} - {Boolean(c.unparseable) && <div>Unparseable: {formatNumber(c.unparseable)}</div>} </div> ); }, show: visibleColumns.shown('Stats'), }, + { + Header: 'Recent errors', + accessor: 'status.payload.recentErrors', + width: 200, + filterable: false, + sortable: false, + show: visibleColumns.shown('Recent errors'), + Cell: ({ value, original }) => { + return ( + <TableClickableCell + onClick={() => this.onSupervisorDetail(original)} + hoverIcon={IconNames.SEARCH_TEMPLATE} + title="See errors" + > + {pluralIfNeeded(value?.length, 'error')} + </TableClickableCell> + ); + }, + }, { Header: ACTION_COLUMN_LABEL, id: ACTION_COLUMN_ID, --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
