Web console: better tooltip when no size is available (#17008)

* better tooltip when no size is available

* better labels for columns

* fix label in segments view
This commit is contained in:
Vadim Ogievetsky 2024-09-05 13:51:03 -07:00 committed by GitHub
parent ba6f804f48
commit dc5c55a836
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 66 additions and 47 deletions

View File

@ -67,17 +67,35 @@ exports[`DatasourcesView matches snapshot 1`] = `
"Availability", "Availability",
"Historical load/drop queues", "Historical load/drop queues",
"Total data size", "Total data size",
"Running tasks", {
"label": "sys.tasks",
"text": "Running tasks",
},
"Segment rows", "Segment rows",
"Segment size", "Segment size",
"Segment granularity", {
"label": "𝑓(sys.segments)",
"text": "Segment granularity",
},
"Total rows", "Total rows",
"Avg. row size", "Avg. row size",
"Replicated size", "Replicated size",
"Compaction", {
"% Compacted", "label": "compaction API",
"Left to be compacted", "text": "Compaction",
"Retention", },
{
"label": "compaction API",
"text": "% Compacted",
},
{
"label": "compaction API",
"text": "Left to be compacted",
},
{
"label": "rules API",
"text": "Retention",
},
] ]
} }
onChange={[Function]} onChange={[Function]}

View File

@ -26,6 +26,7 @@ import type { Filter } from 'react-table';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import { import {
type TableColumnSelectorColumn,
ACTION_COLUMN_ID, ACTION_COLUMN_ID,
ACTION_COLUMN_LABEL, ACTION_COLUMN_LABEL,
ACTION_COLUMN_WIDTH, ACTION_COLUMN_WIDTH,
@ -86,44 +87,44 @@ import { RuleUtil } from '../../utils/load-rule';
import './datasources-view.scss'; import './datasources-view.scss';
const tableColumns: Record<CapabilitiesMode, string[]> = { const TABLE_COLUMNS_BY_MODE: Record<CapabilitiesMode, TableColumnSelectorColumn[]> = {
'full': [ 'full': [
'Datasource name', 'Datasource name',
'Availability', 'Availability',
'Historical load/drop queues', 'Historical load/drop queues',
'Total data size', 'Total data size',
'Running tasks', { text: 'Running tasks', label: 'sys.tasks' },
'Segment rows', 'Segment rows',
'Segment size', 'Segment size',
'Segment granularity', { text: 'Segment granularity', label: '𝑓(sys.segments)' },
'Total rows', 'Total rows',
'Avg. row size', 'Avg. row size',
'Replicated size', 'Replicated size',
'Compaction', { text: 'Compaction', label: 'compaction API' },
'% Compacted', { text: '% Compacted', label: 'compaction API' },
'Left to be compacted', { text: 'Left to be compacted', label: 'compaction API' },
'Retention', { text: 'Retention', label: 'rules API' },
], ],
'no-sql': [ 'no-sql': [
'Datasource name', 'Datasource name',
'Availability', 'Availability',
'Historical load/drop queues', 'Historical load/drop queues',
'Total data size', 'Total data size',
'Running tasks', { text: 'Running tasks', label: 'tasks API' },
'Compaction', { text: 'Compaction', label: 'compaction API' },
'% Compacted', { text: '% Compacted', label: 'compaction API' },
'Left to be compacted', { text: 'Left to be compacted', label: 'compaction API' },
'Retention', { text: 'Retention', label: 'rules API' },
], ],
'no-proxy': [ 'no-proxy': [
'Datasource name', 'Datasource name',
'Availability', 'Availability',
'Historical load/drop queues', 'Historical load/drop queues',
'Total data size', 'Total data size',
'Running tasks', { text: 'Running tasks', label: 'sys.tasks' },
'Segment rows', 'Segment rows',
'Segment size', 'Segment size',
'Segment granularity', { text: 'Segment granularity', label: '𝑓(sys.segments)' },
'Total rows', 'Total rows',
'Avg. row size', 'Avg. row size',
'Replicated size', 'Replicated size',
@ -517,21 +518,18 @@ GROUP BY 1, 2`;
if (capabilities.hasOverlordAccess()) { if (capabilities.hasOverlordAccess()) {
auxiliaryQueries.push(async (datasourcesAndDefaultRules, cancelToken) => { auxiliaryQueries.push(async (datasourcesAndDefaultRules, cancelToken) => {
try { try {
const runningTasks = await queryDruidSql<RunningTaskRow>( const taskList = (
{ await Api.instance.get(`/druid/indexer/v1/tasks?state=running`, { cancelToken })
query: DatasourcesView.RUNNING_TASK_SQL, ).data;
},
cancelToken,
);
const runningTasksByDatasource = groupByAsMap( const runningTasksByDatasource = groupByAsMap(
runningTasks, taskList,
x => x.datasource, (t: any) => t.dataSource,
xs => xs =>
groupByAsMap( groupByAsMap(
xs, xs,
x => normalizeTaskType(x.type), x => normalizeTaskType(x.type),
ys => sum(ys, y => y.num_running_tasks), ys => ys.length,
), ),
); );
@ -1703,7 +1701,7 @@ GROUP BY 1, 2`;
disabled={!capabilities.hasSqlOrCoordinatorAccess()} disabled={!capabilities.hasSqlOrCoordinatorAccess()}
/> />
<TableColumnSelector <TableColumnSelector
columns={tableColumns[capabilities.getMode()]} columns={TABLE_COLUMNS_BY_MODE[capabilities.getMode()]}
onChange={column => onChange={column =>
this.setState(prevState => ({ this.setState(prevState => ({
visibleColumns: prevState.visibleColumns.toggle(column), visibleColumns: prevState.visibleColumns.toggle(column),

View File

@ -55,7 +55,10 @@ exports[`SegmentsView matches snapshot 1`] = `
"Start", "Start",
"End", "End",
"Version", "Version",
"Time span", {
"label": "𝑓(sys.segments)",
"text": "Time span",
},
"Shard type", "Shard type",
"Shard spec", "Shard spec",
"Partition", "Partition",

View File

@ -26,6 +26,7 @@ import type { Filter } from 'react-table';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import { import {
type TableColumnSelectorColumn,
ACTION_COLUMN_ID, ACTION_COLUMN_ID,
ACTION_COLUMN_LABEL, ACTION_COLUMN_LABEL,
ACTION_COLUMN_WIDTH, ACTION_COLUMN_WIDTH,
@ -76,14 +77,14 @@ import type { BasicAction } from '../../utils/basic-action';
import './segments-view.scss'; import './segments-view.scss';
const tableColumns: Record<CapabilitiesMode, string[]> = { const TABLE_COLUMNS_BY_MODE: Record<CapabilitiesMode, TableColumnSelectorColumn[]> = {
'full': [ 'full': [
'Segment ID', 'Segment ID',
'Datasource', 'Datasource',
'Start', 'Start',
'End', 'End',
'Version', 'Version',
'Time span', { text: 'Time span', label: '𝑓(sys.segments)' },
'Shard type', 'Shard type',
'Shard spec', 'Shard spec',
'Partition', 'Partition',
@ -105,6 +106,7 @@ const tableColumns: Record<CapabilitiesMode, string[]> = {
'Start', 'Start',
'End', 'End',
'Version', 'Version',
{ text: 'Time span', label: '𝑓(sys.segments)' },
'Shard type', 'Shard type',
'Shard spec', 'Shard spec',
'Partition', 'Partition',
@ -260,7 +262,7 @@ END AS "time_span"`,
if (capabilities.hasSql()) { if (capabilities.hasSql()) {
const whereParts = filterMap(filtered, (f: Filter) => { const whereParts = filterMap(filtered, (f: Filter) => {
if (f.id === 'shard_type') { if (f.id === 'shard_type') {
// Special handling for shard_type that needs to be search in the shard_spec // Special handling for shard_type that needs to be searched for in the shard_spec
// Creates filters like `shard_spec LIKE '%"type":"numbered"%'` // Creates filters like `shard_spec LIKE '%"type":"numbered"%'`
const modeAndNeedle = parseFilterModeAndNeedle(f); const modeAndNeedle = parseFilterModeAndNeedle(f);
if (!modeAndNeedle) return; if (!modeAndNeedle) return;
@ -1008,7 +1010,7 @@ END AS "time_span"`,
disabled={!capabilities.hasSqlOrCoordinatorAccess()} disabled={!capabilities.hasSqlOrCoordinatorAccess()}
/> />
<TableColumnSelector <TableColumnSelector
columns={tableColumns[capabilities.getMode()]} columns={TABLE_COLUMNS_BY_MODE[capabilities.getMode()]}
onChange={column => onChange={column =>
this.setState(prevState => ({ this.setState(prevState => ({
visibleColumns: prevState.visibleColumns.toggle(column), visibleColumns: prevState.visibleColumns.toggle(column),

View File

@ -24,6 +24,7 @@ import type { Filter } from 'react-table';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import { import {
type TableColumnSelectorColumn,
ACTION_COLUMN_ID, ACTION_COLUMN_ID,
ACTION_COLUMN_LABEL, ACTION_COLUMN_LABEL,
ACTION_COLUMN_WIDTH, ACTION_COLUMN_WIDTH,
@ -59,7 +60,7 @@ import type { BasicAction } from '../../utils/basic-action';
import './services-view.scss'; import './services-view.scss';
const tableColumns: Record<CapabilitiesMode, string[]> = { const TABLE_COLUMNS_BY_MODE: Record<CapabilitiesMode, TableColumnSelectorColumn[]> = {
'full': [ 'full': [
'Service', 'Service',
'Type', 'Type',
@ -804,7 +805,7 @@ ORDER BY
/> />
{this.renderBulkServicesActions()} {this.renderBulkServicesActions()}
<TableColumnSelector <TableColumnSelector
columns={tableColumns[capabilities.getMode()]} columns={TABLE_COLUMNS_BY_MODE[capabilities.getMode()]}
onChange={column => onChange={column =>
this.setState(prevState => ({ this.setState(prevState => ({
visibleColumns: prevState.visibleColumns.toggle(column), visibleColumns: prevState.visibleColumns.toggle(column),

View File

@ -61,6 +61,7 @@ import './execution-stages-pane.scss';
const MAX_STAGE_ROWS = 20; const MAX_STAGE_ROWS = 20;
const MAX_DETAIL_ROWS = 20; const MAX_DETAIL_ROWS = 20;
const NOT_SIZE_ON_DISK = '(does not represent size on disk)'; const NOT_SIZE_ON_DISK = '(does not represent size on disk)';
const NO_SIZE_INFO = 'no size info';
function summarizeTableInput(tableStageInput: StageInput): string { function summarizeTableInput(tableStageInput: StageInput): string {
if (tableStageInput.type !== 'table') return ''; if (tableStageInput.type !== 'table') return '';
@ -271,7 +272,7 @@ export const ExecutionStagesPane = React.memo(function ExecutionStagesPane(
title={ title={
c.bytes c.bytes
? `Uncompressed size: ${formatBytesCompact(c.bytes)} ${NOT_SIZE_ON_DISK}` ? `Uncompressed size: ${formatBytesCompact(c.bytes)} ${NOT_SIZE_ON_DISK}`
: undefined : NO_SIZE_INFO
} }
/> />
{Boolean(c.totalFiles) && ( {Boolean(c.totalFiles) && (
@ -378,7 +379,7 @@ export const ExecutionStagesPane = React.memo(function ExecutionStagesPane(
title={ title={
c.bytes c.bytes
? `Uncompressed size: ${formatBytesCompact(c.bytes)} ${NOT_SIZE_ON_DISK}` ? `Uncompressed size: ${formatBytesCompact(c.bytes)} ${NOT_SIZE_ON_DISK}`
: undefined : NO_SIZE_INFO
} }
/> />
); );
@ -395,19 +396,15 @@ export const ExecutionStagesPane = React.memo(function ExecutionStagesPane(
const hasCounter = stages.hasCounterForStage(stage, inputCounter); const hasCounter = stages.hasCounterForStage(stage, inputCounter);
const bytes = stages.getTotalCounterForStage(stage, inputCounter, 'bytes'); const bytes = stages.getTotalCounterForStage(stage, inputCounter, 'bytes');
const inputFileCount = stages.getTotalCounterForStage(stage, inputCounter, 'totalFiles'); const inputFileCount = stages.getTotalCounterForStage(stage, inputCounter, 'totalFiles');
const inputLabel = `${formatInputLabel(stage, inputNumber)} (input${inputNumber})`;
return ( return (
<div <div
className="data-transfer" className="data-transfer"
key={inputNumber} key={inputNumber}
title={ title={
bytes bytes
? `${formatInputLabel( ? `${inputLabel} uncompressed size: ${formatBytesCompact(bytes)} ${NOT_SIZE_ON_DISK}`
stage, : `${inputLabel}: ${NO_SIZE_INFO}`
inputNumber,
)} (input${inputNumber}) uncompressed size: ${formatBytesCompact(
bytes,
)} ${NOT_SIZE_ON_DISK}`
: undefined
} }
> >
<BracedText <BracedText
@ -510,7 +507,7 @@ ${title} uncompressed size: ${formatBytesCompact(
if (!stages.hasCounterForStage(stage, 'segmentGenerationProgress')) return; if (!stages.hasCounterForStage(stage, 'segmentGenerationProgress')) return;
return ( return (
<div className="data-transfer"> <div className="data-transfer" title={NO_SIZE_INFO}>
<BracedText <BracedText
text={formatRows(stages.getTotalSegmentGenerationProgressForStage(stage, field))} text={formatRows(stages.getTotalSegmentGenerationProgressForStage(stage, field))}
braces={rowsValues} braces={rowsValues}