Skip to content

Commit

Permalink
Web console: fix a few console bugs (apache#16735)
Browse files Browse the repository at this point in the history
* remove __time from min max query shortcut

* fix scrolling in retention rules dialog

* actions menus should have titles

* change term

* correctly name sort/shuffle
  • Loading branch information
vogievetsky authored and edgar2020 committed Jul 19, 2024
1 parent 5bdbb0f commit a655259
Show file tree
Hide file tree
Showing 16 changed files with 785 additions and 742 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { ActionCell } from './action-cell';

describe('ActionCell', () => {
it('matches snapshot', () => {
const actionCell = <ActionCell onDetail={() => {}} actions={[]} />;
const actionCell = <ActionCell onDetail={() => {}} actions={[]} menuTitle="item" />;
const { container } = render(actionCell);
expect(container.firstChild).toMatchSnapshot();
});
Expand Down
7 changes: 4 additions & 3 deletions web-console/src/components/action-cell/action-cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ export const ACTION_COLUMN_WIDTH = 70;
export interface ActionCellProps {
onDetail?: () => void;
disableDetail?: boolean;
actions?: BasicAction[];
actions: BasicAction[];
menuTitle: string;
}

export const ActionCell = React.memo(function ActionCell(props: ActionCellProps) {
const { onDetail, disableDetail, actions } = props;
const actionsMenu = actions ? basicActionsToMenu(actions) : null;
const { onDetail, disableDetail, actions, menuTitle } = props;
const actionsMenu = basicActionsToMenu(actions, menuTitle);

return (
<div className="action-cell">
Expand Down

Large diffs are not rendered by default.

18 changes: 17 additions & 1 deletion web-console/src/dialogs/retention-dialog/retention-dialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,26 @@
display: flex;
flex-direction: column;

.rule-editor {
.rule-form {
position: relative;
flex: 1;

.rule-form-content {
position: absolute;
width: 100%;
height: 100%;
overflow: auto;
}
}

.rule-editor:not(:last-child) {
margin-bottom: 15px;
}

.json-input {
margin-bottom: 10px;
}

.no-rules-message {
font-style: italic;
}
Expand Down
112 changes: 60 additions & 52 deletions web-console/src/dialogs/retention-dialog/retention-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,24 @@ ORDER BY 1`,
setCurrentRules(swapElements(currentRules, index, index + direction));
}

const defaultRuleRender =
datasource !== CLUSTER_DEFAULT_FAKE_DATASOURCE ? (
<FormGroup
label={
<>
Cluster defaults (<a onClick={onEditDefaults}>edit</a>)
</>
}
>
<p>The cluster default rules are evaluated if none of the above rules match.</p>
{currentTab === 'form' ? (
defaultRules.map((rule, index) => <RuleEditor key={index} rule={rule} tiers={tiers} />)
) : (
<JsonInput value={defaultRules} />
)}
</FormGroup>
) : undefined;

return (
<SnitchDialog
className="retention-dialog"
Expand Down Expand Up @@ -142,61 +160,51 @@ ORDER BY 1`,
}}
/>
{currentTab === 'form' ? (
<FormGroup>
{currentRules.length ? (
currentRules.map((rule, index) => (
<RuleEditor
key={index}
rule={rule}
tiers={tiers}
onChange={r => changeRule(r, index)}
onDelete={() => deleteRule(index)}
moveUp={index > 0 ? () => moveRule(index, -1) : undefined}
moveDown={index < currentRules.length - 1 ? () => moveRule(index, 1) : undefined}
/>
))
) : datasource !== CLUSTER_DEFAULT_FAKE_DATASOURCE ? (
<p className="no-rules-message">
This datasource currently has no rules, it will use the cluster defaults.
</p>
) : undefined}
<div>
<Button
icon={IconNames.PLUS}
onClick={addRule}
intent={currentRules.length ? undefined : Intent.PRIMARY}
>
New rule
</Button>
<div className="rule-form">
<div className="rule-form-content">
<FormGroup>
{currentRules.length ? (
currentRules.map((rule, index) => (
<RuleEditor
key={index}
rule={rule}
tiers={tiers}
onChange={r => changeRule(r, index)}
onDelete={() => deleteRule(index)}
moveUp={index > 0 ? () => moveRule(index, -1) : undefined}
moveDown={
index < currentRules.length - 1 ? () => moveRule(index, 1) : undefined
}
/>
))
) : datasource !== CLUSTER_DEFAULT_FAKE_DATASOURCE ? (
<p className="no-rules-message">
This datasource currently has no rules, it will use the cluster defaults.
</p>
) : undefined}
<div>
<Button
icon={IconNames.PLUS}
onClick={addRule}
intent={currentRules.length ? undefined : Intent.PRIMARY}
>
New rule
</Button>
</div>
</FormGroup>
{defaultRuleRender && <Divider />}
{defaultRuleRender}
</div>
</FormGroup>
</div>
) : (
<JsonInput
value={currentRules}
onChange={setCurrentRules}
setError={setJsonError}
height="100%"
/>
)}
{datasource !== CLUSTER_DEFAULT_FAKE_DATASOURCE && (
<>
<Divider />
<FormGroup
label={
<>
Cluster defaults (<a onClick={onEditDefaults}>edit</a>)
</>
}
>
<p>The cluster default rules are evaluated if none of the above rules match.</p>
{currentTab === 'form' ? (
defaultRules.map((rule, index) => (
<RuleEditor key={index} rule={rule} tiers={tiers} />
))
) : (
<JsonInput value={defaultRules} />
)}
</FormGroup>
<JsonInput
value={currentRules}
onChange={setCurrentRules}
setError={setJsonError}
height="100%"
/>
{defaultRuleRender}
</>
)}
</SnitchDialog>
Expand Down
6 changes: 2 additions & 4 deletions web-console/src/druid-models/stages/stages.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ describe('Stages', () => {

describe('#getByPartitionCountersForStage', () => {
it('works for input', () => {
expect(STAGES.getByPartitionCountersForStage(STAGES.stages[2], 'input'))
.toMatchInlineSnapshot(`
expect(STAGES.getByPartitionCountersForStage(STAGES.stages[2], 'in')).toMatchInlineSnapshot(`
[
{
"index": 0,
Expand All @@ -45,8 +44,7 @@ describe('Stages', () => {
});

it('works for output', () => {
expect(STAGES.getByPartitionCountersForStage(STAGES.stages[2], 'output'))
.toMatchInlineSnapshot(`
expect(STAGES.getByPartitionCountersForStage(STAGES.stages[2], 'out')).toMatchInlineSnapshot(`
[
{
"index": 0,
Expand Down
59 changes: 30 additions & 29 deletions web-console/src/druid-models/stages/stages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ import { deleteKeys, filterMap, oneOf, zeroDivide } from '../../utils';
import type { InputFormat } from '../input-format/input-format';
import type { InputSource } from '../input-source/input-source';

const SORT_WEIGHT = 0.5;
const READING_INPUT_WITH_SORT_WEIGHT = 1 - SORT_WEIGHT;
const SHUFFLE_WEIGHT = 0.5;
const READING_INPUT_WITH_SHUFFLE_WEIGHT = 1 - SHUFFLE_WEIGHT;

export type InOut = 'in' | 'out';

export type StageInput =
| {
Expand Down Expand Up @@ -261,13 +263,13 @@ export class Stages {
return Stages.stageType(stage) !== 'segmentGenerator';
}

stageHasSort(stage: StageDefinition): boolean {
stageHasShuffle(stage: StageDefinition): boolean {
if (!this.stageHasOutput(stage)) return false;
return Boolean(stage.sort);
return Boolean(stage.sort) || this.hasCounterForStage(stage, 'shuffle');
}

stageOutputCounterName(stage: StageDefinition): ChannelCounterName {
return this.stageHasSort(stage) ? 'shuffle' : 'output';
stageFinalCounterName(stage: StageDefinition): ChannelCounterName {
return this.stageHasShuffle(stage) ? 'shuffle' : 'output';
}

overallProgress(): number {
Expand All @@ -286,12 +288,14 @@ export class Stages {
switch (stage.phase) {
case 'READING_INPUT':
return (
(this.stageHasSort(stage) ? READING_INPUT_WITH_SORT_WEIGHT : 1) *
(this.stageHasShuffle(stage) ? READING_INPUT_WITH_SHUFFLE_WEIGHT : 1) *
this.readingInputPhaseProgress(stage)
);

case 'POST_READING':
return READING_INPUT_WITH_SORT_WEIGHT + SORT_WEIGHT * this.postReadingPhaseProgress(stage);
return (
READING_INPUT_WITH_SHUFFLE_WEIGHT + SHUFFLE_WEIGHT * this.postReadingPhaseProgress(stage)
);

case 'RESULTS_READY':
case 'FINISHED':
Expand Down Expand Up @@ -415,7 +419,7 @@ export class Stages {
}

getTotalOutputForStage(stage: StageDefinition, field: 'frames' | 'rows' | 'bytes'): number {
return this.getTotalCounterForStage(stage, this.stageOutputCounterName(stage), field);
return this.getTotalCounterForStage(stage, this.stageFinalCounterName(stage), field);
}

getSortProgressForStage(stage: StageDefinition): number {
Expand Down Expand Up @@ -445,7 +449,7 @@ export class Stages {

const channelCounters = definition.input.map((_, i) => `input${i}` as ChannelCounterName);
if (this.stageHasOutput(stage)) channelCounters.push('output');
if (this.stageHasSort(stage)) channelCounters.push('shuffle');
if (this.stageHasShuffle(stage)) channelCounters.push('shuffle');
return channelCounters;
}

Expand Down Expand Up @@ -479,41 +483,38 @@ export class Stages {

getPartitionChannelCounterNamesForStage(
stage: StageDefinition,
type: 'input' | 'output',
inOut: InOut,
): ChannelCounterName[] {
if (type === 'input') {
if (inOut === 'in') {
const { input, broadcast } = stage.definition;
return filterMap(input, (input, i) =>
input.type === 'stage' && !broadcast?.includes(i)
? (`input${i}` as ChannelCounterName)
: undefined,
);
} else {
return [this.stageOutputCounterName(stage)];
return [this.stageFinalCounterName(stage)];
}
}

getByPartitionCountersForStage(
stage: StageDefinition,
type: 'input' | 'output',
): SimpleWideCounter[] {
const counterNames = this.getPartitionChannelCounterNamesForStage(stage, type);
getByPartitionCountersForStage(stage: StageDefinition, inOut: InOut): SimpleWideCounter[] {
const counterNames = this.getPartitionChannelCounterNamesForStage(stage, inOut);
if (!counterNames.length) return [];

if (!this.hasCounterForStage(stage, counterNames[0])) return [];
const stageCounters = this.getCountersForStage(stage);

const { partitionCount } = stage;
const partitionNumber =
type === 'output'
? partitionCount
: max(stageCounters, stageCounter =>
max(counterNames, counterName => {
const channelCounter = stageCounter[counterName];
if (channelCounter?.type !== 'channel') return 0;
return channelCounter.rows?.length || 0;
}),
);
let partitionNumber = max(stageCounters, stageCounter =>
max(counterNames, counterName => {
const channelCounter = stageCounter[counterName];
if (channelCounter?.type !== 'channel') return 0;
return channelCounter.rows?.length || 0;
}),
);

if (inOut === 'out') {
partitionNumber = Math.max(partitionNumber || 0, stage.partitionCount || 0);
}

if (!partitionNumber) return [];

Expand Down
8 changes: 6 additions & 2 deletions web-console/src/utils/basic-action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

import type { IconName, Intent } from '@blueprintjs/core';
import { Menu, MenuItem } from '@blueprintjs/core';
import { Menu, MenuDivider, MenuItem } from '@blueprintjs/core';
import type { JSX } from 'react';
import React from 'react';

Expand All @@ -29,10 +29,14 @@ export interface BasicAction {
disabledReason?: string;
}

export function basicActionsToMenu(basicActions: BasicAction[]): JSX.Element | undefined {
export function basicActionsToMenu(
basicActions: BasicAction[],
title?: string,
): JSX.Element | undefined {
if (!basicActions.length) return;
return (
<Menu>
{title && <MenuDivider title={title} />}
{basicActions.map(({ icon, title, intent, onAction, disabledReason }, i) => (
<MenuItem
key={i}
Expand Down
5 changes: 3 additions & 2 deletions web-console/src/views/datasources-view/datasources-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ GROUP BY 1, 2`;
num_segments !== num_zero_replica_segments
? `Fully ${descriptor}`
: undefined,
hasZeroReplicationRule ? `${percentZeroReplica}% async only` : '',
hasZeroReplicationRule ? `${percentZeroReplica}% deep storage only` : '',
).join(', ')}{' '}
({segmentsEl})
</span>
Expand All @@ -1228,7 +1228,7 @@ GROUP BY 1, 2`;
{numAvailableSegments ? '\u25cf' : '\u25cb'}&nbsp;
</span>
{`${percentAvailable}% ${descriptor}${
hasZeroReplicationRule ? `, ${percentZeroReplica}% async only` : ''
hasZeroReplicationRule ? `, ${percentZeroReplica}% deep storage only` : ''
}`}{' '}
({segmentsEl})
</span>
Expand Down Expand Up @@ -1614,6 +1614,7 @@ GROUP BY 1, 2`;
}}
disableDetail={unused}
actions={datasourceActions}
menuTitle={datasource}
/>
);
},
Expand Down
1 change: 1 addition & 0 deletions web-console/src/views/lookups-view/lookups-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
this.onDetail(original);
}}
actions={lookupActions}
menuTitle={lookupId}
/>
);
},
Expand Down
1 change: 1 addition & 0 deletions web-console/src/views/segments-view/segments-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,7 @@ END AS "time_span"`,
this.onDetail(id, datasource);
}}
actions={this.getSegmentActions(id, datasource)}
menuTitle={id}
/>
);
},
Expand Down
Loading

0 comments on commit a655259

Please sign in to comment.