This is an automated email from the ASF dual-hosted git repository. vogievetsky pushed a commit to branch segment_timeline2 in repository https://gitbox.apache.org/repos/asf/druid.git
commit f320470b244a5e4008c9c7cbd4db8bf58d608c0e Author: Vadim Ogievetsky <[email protected]> AuthorDate: Fri Nov 1 00:10:18 2024 -0700 fix supervisor view --- .../segment-timeline/segment-bar-chart-render.tsx | 34 +---------- .../datasource/datasource.ts} | 57 +++++++++---------- web-console/src/druid-models/index.ts | 1 + web-console/src/react-table/react-table-utils.ts | 7 ++- .../views/datasources-view/datasources-view.scss | 7 +++ .../views/datasources-view/datasources-view.tsx | 16 ++++-- .../src/views/segments-view/segments-view.tsx | 42 ++++++++++---- .../views/supervisors-view/supervisors-view.tsx | 65 ++++++++++++++-------- 8 files changed, 128 insertions(+), 101 deletions(-) diff --git a/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx b/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx index 44407e22233..026b6e7d0d8 100644 --- a/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx +++ b/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx @@ -25,6 +25,7 @@ import { scaleLinear, scaleUtc } from 'd3-scale'; import type React from 'react'; import { useMemo, useRef, useState } from 'react'; +import { getDatasourceColor } from '../../druid-models'; import { useGlobalEventListener } from '../../hooks'; import { capitalizeFirst, @@ -36,7 +37,6 @@ import { formatInteger, formatNumber, groupBy, - hashJoaat, prettyFormatIsoDate, TZ_UTC, } from '../../utils'; @@ -63,30 +63,6 @@ const POSSIBLE_GRANULARITIES = [ const EXTEND_DATE_RANGE_BY = 0; const EXTEND_X_SCALE_DOMAIN_BY = 10; -const COLORS = [ - '#b33040', - '#d25c4d', - '#f2b447', - '#d9d574', - '#4FAA7E', - '#57ceff', - '#789113', - '#098777', - '#b33040', - '#d2757b', - '#f29063', - '#d9a241', - '#80aa61', - '#c4ff9e', - '#915412', - '#87606c', -]; - -const COLORIZER = ({ datasource }: IntervalBar) => { - const hash = hashJoaat(datasource); - return COLORS[hash % COLORS.length]; -}; - function offsetDateRange(dateRange: NonNullDateRange, offset: number): NonNullDateRange { return [new Date(dateRange[0].valueOf() + offset), new Date(dateRange[1].valueOf() + offset)]; } @@ -390,12 +366,8 @@ export const SegmentBarChartRender = function SegmentBarChartRender( key={i} className="bar-unit" {...segmentBarToRect(intervalBar)} - style={{ fill: COLORIZER(intervalBar) }} - onClick={ - intervalBar.datasource - ? () => changeFocusDatasource(intervalBar.datasource) - : undefined - } + style={{ fill: getDatasourceColor(intervalBar.datasource) }} + onClick={() => changeFocusDatasource(intervalBar.datasource)} onMouseOver={() => { if (mouseDownAt) return; setHoverOn(intervalBar); diff --git a/web-console/src/views/datasources-view/datasources-view.scss b/web-console/src/druid-models/datasource/datasource.ts similarity index 59% copy from web-console/src/views/datasources-view/datasources-view.scss copy to web-console/src/druid-models/datasource/datasource.ts index 1f9d9df328e..313d711b35c 100644 --- a/web-console/src/views/datasources-view/datasources-view.scss +++ b/web-console/src/druid-models/datasource/datasource.ts @@ -16,37 +16,32 @@ * limitations under the License. */ -@import '../../variables'; +import { hashJoaat } from '../../utils'; -.datasources-view { - height: 100%; - width: 100%; - overflow: auto; +const COLORS = [ + '#1f77b4', + '#aec7e8', + '#ff7f0e', + '#ffbb78', + '#2ca02c', + '#98df8a', + '#d62728', + '#ff9896', + '#9467bd', + '#c5b0d5', + '#8c564b', + '#c49c94', + '#e377c2', + '#f7b6d2', + '#7f7f7f', + '#c7c7c7', + '#bcbd22', + '#dbdb8d', + '#17becf', + '#9edae5', +]; - .splitter-layout.timeline-datasources-splitter { - position: absolute; - top: $view-control-bar-height + $standard-padding; - bottom: 0; - width: 100%; - - & > .layout-splitter:hover { - background: black; - opacity: 0.1; - border-radius: 2px; - } - } - - .segment-timeline { - position: absolute; - width: 100%; - height: 100%; - } - - .ReactTable { - @include pin-full; - - .clickable-cell { - cursor: pointer; - } - } +export function getDatasourceColor(datasource: string) { + const hash = hashJoaat(datasource); + return COLORS[hash % COLORS.length]; } diff --git a/web-console/src/druid-models/index.ts b/web-console/src/druid-models/index.ts index 3e5c7062232..e31eedeea73 100644 --- a/web-console/src/druid-models/index.ts +++ b/web-console/src/druid-models/index.ts @@ -21,6 +21,7 @@ export * from './compaction-config/compaction-config'; export * from './compaction-status/compaction-status'; export * from './coordinator-dynamic-config/coordinator-dynamic-config'; export * from './dart/dart-query-entry'; +export * from './datasource/datasource'; export * from './dimension-spec/dimension-spec'; export * from './druid-engine/druid-engine'; export * from './execution/execution'; diff --git a/web-console/src/react-table/react-table-utils.ts b/web-console/src/react-table/react-table-utils.ts index ce9497a1209..dc3debdf32c 100644 --- a/web-console/src/react-table/react-table-utils.ts +++ b/web-console/src/react-table/react-table-utils.ts @@ -18,8 +18,7 @@ import type { IconName } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; -import type { SqlExpression } from '@druid-toolkit/query'; -import { C, F } from '@druid-toolkit/query'; +import { C, F, SqlExpression } from '@druid-toolkit/query'; import type { Filter } from 'react-table'; import { addOrUpdate, caseInsensitiveContains, filterMap } from '../utils'; @@ -153,6 +152,10 @@ export function sqlQueryCustomTableFilter(filter: Filter): SqlExpression | undef } } +export function sqlQueryCustomTableFilters(filters: Filter[]): SqlExpression { + return SqlExpression.and(...filterMap(filters, sqlQueryCustomTableFilter)); +} + export function tableFiltersToString(tableFilters: Filter[]): string { return tableFilters .map(({ id, value }) => `${id}${value.replace(/[&%]/g, encodeURIComponent)}`) diff --git a/web-console/src/views/datasources-view/datasources-view.scss b/web-console/src/views/datasources-view/datasources-view.scss index 1f9d9df328e..91a95bc32a0 100644 --- a/web-console/src/views/datasources-view/datasources-view.scss +++ b/web-console/src/views/datasources-view/datasources-view.scss @@ -45,6 +45,13 @@ .ReactTable { @include pin-full; + .datasource-color-tab { + display: inline-block; + width: 10px; + height: 20px; + border-radius: 3px; + } + .clickable-cell { cursor: pointer; } diff --git a/web-console/src/views/datasources-view/datasources-view.tsx b/web-console/src/views/datasources-view/datasources-view.tsx index 4477540af66..f58caee1ae8 100644 --- a/web-console/src/views/datasources-view/datasources-view.tsx +++ b/web-console/src/views/datasources-view/datasources-view.tsx @@ -56,6 +56,7 @@ import type { import { END_OF_TIME_DATE, formatCompactionInfo, + getDatasourceColor, RuleUtil, START_OF_TIME_DATE, zeroCompactionStatus, @@ -1145,7 +1146,8 @@ GROUP BY 1, 2`; private renderDatasourcesTable() { const { goToSegments, goToTasks, capabilities, filters, onFiltersChange } = this.props; - const { datasourcesAndDefaultRulesState, showUnused, visibleColumns } = this.state; + const { datasourcesAndDefaultRulesState, showUnused, visibleColumns, showSegmentTimeline } = + this.state; let { datasources, defaultRules } = datasourcesAndDefaultRulesState.data || { datasources: [] }; @@ -1199,12 +1201,18 @@ GROUP BY 1, 2`; show: visibleColumns.shown('Datasource name'), accessor: 'datasource', width: 150, - Cell: row => ( + Cell: ({ value, original }) => ( <TableClickableCell - onClick={() => this.onDetail(row.original)} + onClick={() => this.onDetail(original)} hoverIcon={IconNames.SEARCH_TEMPLATE} > - {row.value} + {showSegmentTimeline ? ( + <> + <span style={{ color: getDatasourceColor(value) }}>■</span> {value} + </> + ) : ( + value + )} </TableClickableCell> ), }, diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx index 18dcc4c4cc3..0f7751bf76c 100644 --- a/web-console/src/views/segments-view/segments-view.tsx +++ b/web-console/src/views/segments-view/segments-view.tsx @@ -20,6 +20,7 @@ import { Button, ButtonGroup, Intent, Label, MenuItem, Switch, Tag } from '@blue import { IconNames } from '@blueprintjs/icons'; import { C, L, SqlComparison, SqlExpression } from '@druid-toolkit/query'; import * as JSONBig from 'json-bigint-native'; +import type { ReactNode } from 'react'; import React from 'react'; import type { Filter, SortingRule } from 'react-table'; import ReactTable from 'react-table'; @@ -44,7 +45,7 @@ import { AsyncActionDialog } from '../../dialogs'; import { SegmentTableActionDialog } from '../../dialogs/segments-table-action-dialog/segment-table-action-dialog'; import { ShowValueDialog } from '../../dialogs/show-value-dialog/show-value-dialog'; import type { QueryWithContext } from '../../druid-models'; -import { computeSegmentTimeSpan } from '../../druid-models'; +import { computeSegmentTimeSpan, getDatasourceColor } from '../../druid-models'; import type { Capabilities, CapabilitiesMode } from '../../helpers'; import { booleanCustomTableFilter, @@ -275,8 +276,6 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment if (capabilities.hasSql()) { const whereExpression = segmentFiltersToExpression(filtered); - let queryParts: string[]; - let filterClause = ''; if (whereExpression.toString() !== 'TRUE') { filterClause = whereExpression.toString(); @@ -296,6 +295,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment const base = SegmentsView.baseQuery(visibleColumns); const orderByClause = sortedToOrderByClause(effectiveSorted); + let queryParts: string[]; if (groupByInterval) { const innerQuery = compact([ `SELECT "start", "end"`, @@ -476,7 +476,11 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment }); } - private renderFilterableCell(field: string, enableComparisons = false) { + private renderFilterableCell( + field: string, + enableComparisons = false, + valueFn: (value: string) => ReactNode = String, + ) { const { filters, onFiltersChange } = this.props; // eslint-disable-next-line react/display-name @@ -488,14 +492,22 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment onFiltersChange={onFiltersChange} enableComparisons={enableComparisons} > - {row.value} + {valueFn(row.value)} </TableFilterableCell> ); } renderSegmentsTable() { const { capabilities, filters, onFiltersChange } = this.props; - const { segmentsState, visibleColumns, groupByInterval, page, pageSize, sorted } = this.state; + const { + segmentsState, + visibleColumns, + groupByInterval, + page, + pageSize, + sorted, + showSegmentTimeline, + } = this.state; const segments = segmentsState.data || []; @@ -530,15 +542,15 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment onFilteredChange={onFiltersChange} sorted={sorted} onSortedChange={sorted => this.setState({ sorted })} - showPageJump={false} - ofText="" - pivotBy={groupByInterval ? ['interval'] : []} page={page} onPageChange={page => this.setState({ page })} pageSize={pageSize} onPageSizeChange={pageSize => this.setState({ pageSize })} pageSizeOptions={STANDARD_TABLE_PAGE_SIZE_OPTIONS} showPagination + showPageJump={false} + ofText="" + pivotBy={groupByInterval ? ['interval'] : []} columns={[ { Header: 'Segment ID', @@ -561,7 +573,17 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment show: visibleColumns.shown('Datasource'), accessor: 'datasource', width: 140, - Cell: this.renderFilterableCell('datasource'), + Cell: this.renderFilterableCell( + 'datasource', + false, + showSegmentTimeline + ? value => ( + <> + <span style={{ color: getDatasourceColor(value) }}>■</span> {value} + </> + ) + : String, + ), }, { Header: 'Interval', diff --git a/web-console/src/views/supervisors-view/supervisors-view.tsx b/web-console/src/views/supervisors-view/supervisors-view.tsx index 2474782e0ae..8c557fe3c54 100644 --- a/web-console/src/views/supervisors-view/supervisors-view.tsx +++ b/web-console/src/views/supervisors-view/supervisors-view.tsx @@ -18,11 +18,10 @@ import { Icon, Intent, Menu, MenuItem, Popover, Position, Tag } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; -import { SqlExpression } from '@druid-toolkit/query'; import * as JSONBig from 'json-bigint-native'; import type { JSX } from 'react'; import React from 'react'; -import type { Filter } from 'react-table'; +import type { Filter, SortingRule } from 'react-table'; import ReactTable from 'react-table'; import type { TableColumnSelectorColumn } from '../../components'; @@ -58,7 +57,7 @@ import type { Capabilities } from '../../helpers'; import { SMALL_TABLE_PAGE_SIZE, SMALL_TABLE_PAGE_SIZE_OPTIONS, - sqlQueryCustomTableFilter, + sqlQueryCustomTableFilters, } from '../../react-table'; import { Api, AppToaster } from '../../singletons'; import type { AuxiliaryQueryFn, TableState } from '../../utils'; @@ -67,7 +66,6 @@ import { changeByIndex, checkedCircleIcon, deepGet, - filterMap, formatByteRate, formatBytes, formatInteger, @@ -162,7 +160,11 @@ export interface SupervisorsViewState { supervisorTableActionDialogId?: string; supervisorTableActionDialogActions: BasicAction[]; + visibleColumns: LocalStorageBackedVisibility; + page: number; + pageSize: number; + sorted: SortingRule[]; } function detailedStateToColor(detailedState: string): string { @@ -223,6 +225,9 @@ export class SupervisorsView extends React.PureComponent< visibleColumns: new LocalStorageBackedVisibility( LocalStorageKeys.SUPERVISOR_TABLE_COLUMN_SELECTION, ), + page: 0, + pageSize: SMALL_TABLE_PAGE_SIZE, + sorted: [], }; this.supervisorQueryManager = new QueryManager({ @@ -233,10 +238,11 @@ export class SupervisorsView extends React.PureComponent< ) => { let supervisors: SupervisorQueryResultRow[]; if (capabilities.hasSql()) { + const whereExpression = sqlQueryCustomTableFilters(filtered); + let filterClause = ''; - const whereParts = filterMap(filtered, sqlQueryCustomTableFilter); - if (whereParts.length) { - filterClause = SqlExpression.and(...whereParts).toString(); + if (whereExpression.toString() !== 'TRUE') { + filterClause = whereExpression.toString(); } const sqlQuery = assemble( @@ -355,22 +361,33 @@ export class SupervisorsView extends React.PureComponent< }); } - private lastTableState: TableState | undefined; - componentWillUnmount(): void { this.supervisorQueryManager.terminate(); } - private readonly fetchData = (tableState?: TableState) => { - const { capabilities } = this.props; - const { visibleColumns } = this.state; - if (tableState) this.lastTableState = tableState; - if (!this.lastTableState) return; - const { page, pageSize, filtered, sorted } = this.lastTableState; + componentDidUpdate( + prevProps: Readonly<SupervisorsViewProps>, + prevState: Readonly<SupervisorsViewState>, + ) { + const { filters } = this.props; + const { page, pageSize, sorted } = this.state; + if ( + !sqlQueryCustomTableFilters(filters).equals(sqlQueryCustomTableFilters(prevProps.filters)) || + page !== prevState.page || + pageSize !== prevState.pageSize || + sortedToOrderByClause(sorted) !== sortedToOrderByClause(prevState.sorted) + ) { + this.fetchData(); + } + } + + private readonly fetchData = () => { + const { capabilities, filters } = this.props; + const { visibleColumns, page, pageSize, sorted } = this.state; this.supervisorQueryManager.runQuery({ page, pageSize, - filtered, + filtered: filters, sorted, visibleColumns, capabilities, @@ -663,7 +680,7 @@ export class SupervisorsView extends React.PureComponent< private renderSupervisorTable() { const { goToTasks, filters, onFiltersChange } = this.props; - const { supervisorsState, statsKey, visibleColumns } = this.state; + const { supervisorsState, statsKey, visibleColumns, page, pageSize, sorted } = this.state; const supervisors = supervisorsState.data || []; return ( @@ -678,14 +695,16 @@ export class SupervisorsView extends React.PureComponent< filterable filtered={filters} onFilteredChange={onFiltersChange} - onFetchData={tableState => { - this.fetchData(tableState); - }} - showPageJump={false} - ofText="" - defaultPageSize={SMALL_TABLE_PAGE_SIZE} + sorted={sorted} + onSortedChange={sorted => this.setState({ sorted })} + page={page} + onPageChange={page => this.setState({ page })} + pageSize={pageSize} + onPageSizeChange={pageSize => this.setState({ pageSize })} pageSizeOptions={SMALL_TABLE_PAGE_SIZE_OPTIONS} showPagination={supervisors.length > SMALL_TABLE_PAGE_SIZE} + showPageJump={false} + ofText="" columns={[ { Header: twoLines('Supervisor ID', <i>(datasource)</i>), --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
