mirror of https://github.com/apache/druid.git
Web console: Compaction history dialog (#13861)
* initial renames * add comaction history diff * final fixes * update snapshots * use maps * update test
This commit is contained in:
parent
cd4ad5123a
commit
38b6373bf7
|
@ -5693,7 +5693,7 @@ license_category: binary
|
|||
module: web-console
|
||||
license_name: Apache License version 2.0
|
||||
copyright: Imply Data
|
||||
version: 0.17.4
|
||||
version: 0.17.5
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ export class DatasourcesOverview {
|
|||
private async openCompactionConfigurationDialog(datasourceName: string): Promise<void> {
|
||||
await this.openEditActions(datasourceName);
|
||||
await this.clickMenuItem('Edit compaction configuration');
|
||||
await this.page.waitForSelector('div.compaction-dialog');
|
||||
await this.page.waitForSelector('div.compaction-config-dialog');
|
||||
}
|
||||
|
||||
private async clickMenuItem(text: string): Promise<void> {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"d3-axis": "^2.1.0",
|
||||
"d3-scale": "^3.3.0",
|
||||
"d3-selection": "^2.0.0",
|
||||
"druid-query-toolkit": "^0.17.4",
|
||||
"druid-query-toolkit": "^0.17.5",
|
||||
"file-saver": "^2.0.2",
|
||||
"follow-redirects": "^1.14.7",
|
||||
"fontsource-open-sans": "^3.0.9",
|
||||
|
@ -8542,9 +8542,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/druid-query-toolkit": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.4.tgz",
|
||||
"integrity": "sha512-d/mNJ9ausAfxQaxgGWfP4dHpwcIqjkbdz1NNmTk6DHMbwlskk96MnXrvPsOCmeoKMb0iYVeVtq9CbB1vNaPZ5A==",
|
||||
"version": "0.17.5",
|
||||
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.5.tgz",
|
||||
"integrity": "sha512-N+kqu6xy2Gd3zQwNbBxQXG+qjU7jzbCXvE84uxsIh5gxRbiKAEOsyWtKrWF/DZZ91g/WSIMQih5klkmmcjlVfQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.1"
|
||||
},
|
||||
|
@ -33236,9 +33236,9 @@
|
|||
}
|
||||
},
|
||||
"druid-query-toolkit": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.4.tgz",
|
||||
"integrity": "sha512-d/mNJ9ausAfxQaxgGWfP4dHpwcIqjkbdz1NNmTk6DHMbwlskk96MnXrvPsOCmeoKMb0iYVeVtq9CbB1vNaPZ5A==",
|
||||
"version": "0.17.5",
|
||||
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.5.tgz",
|
||||
"integrity": "sha512-N+kqu6xy2Gd3zQwNbBxQXG+qjU7jzbCXvE84uxsIh5gxRbiKAEOsyWtKrWF/DZZ91g/WSIMQih5klkmmcjlVfQ==",
|
||||
"requires": {
|
||||
"tslib": "^2.3.1"
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
"d3-axis": "^2.1.0",
|
||||
"d3-scale": "^3.3.0",
|
||||
"d3-selection": "^2.0.0",
|
||||
"druid-query-toolkit": "^0.17.4",
|
||||
"druid-query-toolkit": "^0.17.5",
|
||||
"file-saver": "^2.0.2",
|
||||
"follow-redirects": "^1.14.7",
|
||||
"fontsource-open-sans": "^3.0.9",
|
||||
|
|
|
@ -90,7 +90,7 @@ checker.init(
|
|||
|
||||
Object.assign(packages, extraPackages);
|
||||
|
||||
const seen = {};
|
||||
const seen = new Map();
|
||||
const mapped = Object.keys(packages)
|
||||
.sort()
|
||||
.map(p => {
|
||||
|
@ -98,8 +98,8 @@ checker.init(
|
|||
if (!m) throw new Error(`Malformed name@version`);
|
||||
const name = m[1];
|
||||
if (name === 'web-console') return; // This is me!
|
||||
if (seen[name]) return; // Dedupe
|
||||
seen[name] = true;
|
||||
if (seen.has(name)) return; // Dedupe
|
||||
seen.set(name, true);
|
||||
|
||||
const version = m[2];
|
||||
const meta = packages[p];
|
||||
|
|
|
@ -51,6 +51,7 @@ export * from './show-json/show-json';
|
|||
export * from './show-log/show-log';
|
||||
export * from './show-value/show-value';
|
||||
export * from './suggestion-menu/suggestion-menu';
|
||||
export * from './supervisor-history-panel/supervisor-history-panel';
|
||||
export * from './table-cell/table-cell';
|
||||
export * from './table-cell-unparseable/table-cell-unparseable';
|
||||
export * from './table-clickable-cell/table-clickable-cell';
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
exports[`ShowValue matches snapshot 1`] = `
|
||||
<div
|
||||
class="show-json"
|
||||
class="show-value"
|
||||
>
|
||||
<div
|
||||
class="top-actions"
|
||||
|
|
|
@ -32,7 +32,7 @@ export interface ShowValueProps {
|
|||
export const ShowValue = React.memo(function ShowValue(props: ShowValueProps) {
|
||||
const { jsonValue, onDiffWithPrevious, downloadFilename } = props;
|
||||
return (
|
||||
<div className="show-json">
|
||||
<div className="show-value">
|
||||
{(onDiffWithPrevious || downloadFilename) && (
|
||||
<div className="top-actions">
|
||||
<ButtonGroup className="right-buttons">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ShowHistory matches snapshot 1`] = `
|
||||
exports[`SupervisorHistoryPanel matches snapshot 1`] = `
|
||||
<div
|
||||
class="loader"
|
||||
>
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
@import '../../variables';
|
||||
|
||||
.show-history {
|
||||
.supervisor-history-panel {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
|
@ -19,11 +19,11 @@
|
|||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { ShowHistory } from './show-history';
|
||||
import { SupervisorHistoryPanel } from './supervisor-history-panel';
|
||||
|
||||
describe('ShowHistory', () => {
|
||||
describe('SupervisorHistoryPanel', () => {
|
||||
it('matches snapshot', () => {
|
||||
const showJson = <ShowHistory endpoint="test" downloadFilenamePrefix="test" />;
|
||||
const showJson = <SupervisorHistoryPanel supervisorId="test" />;
|
||||
const { container } = render(showJson);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
|
@ -29,29 +29,34 @@ import { deepSet } from '../../utils';
|
|||
import { Loader } from '../loader/loader';
|
||||
import { ShowValue } from '../show-value/show-value';
|
||||
|
||||
import './show-history.scss';
|
||||
import './supervisor-history-panel.scss';
|
||||
|
||||
export interface VersionSpec {
|
||||
export interface SupervisorHistoryEntry {
|
||||
version: string;
|
||||
spec: IngestionSpec;
|
||||
}
|
||||
|
||||
export interface ShowHistoryProps {
|
||||
endpoint: string;
|
||||
downloadFilenamePrefix?: string;
|
||||
export interface SupervisorHistoryPanelProps {
|
||||
supervisorId: string;
|
||||
}
|
||||
|
||||
export const ShowHistory = React.memo(function ShowHistory(props: ShowHistoryProps) {
|
||||
const { downloadFilenamePrefix, endpoint } = props;
|
||||
export const SupervisorHistoryPanel = React.memo(function SupervisorHistoryPanel(
|
||||
props: SupervisorHistoryPanelProps,
|
||||
) {
|
||||
const { supervisorId } = props;
|
||||
|
||||
const [historyState] = useQueryManager<string, VersionSpec[]>({
|
||||
processQuery: async (endpoint: string) => {
|
||||
const resp = await Api.instance.get(endpoint);
|
||||
return resp.data.map((vs: VersionSpec) => deepSet(vs, 'spec', cleanSpec(vs.spec, true)));
|
||||
},
|
||||
initQuery: endpoint,
|
||||
});
|
||||
const [diffIndex, setDiffIndex] = useState(-1);
|
||||
const [historyState] = useQueryManager<string, SupervisorHistoryEntry[]>({
|
||||
initQuery: supervisorId,
|
||||
processQuery: async supervisorId => {
|
||||
const resp = await Api.instance.get(
|
||||
`/druid/indexer/v1/supervisor/${Api.encodePath(supervisorId)}/history`,
|
||||
);
|
||||
return resp.data.map((vs: SupervisorHistoryEntry) =>
|
||||
deepSet(vs, 'spec', cleanSpec(vs.spec, true)),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
if (historyState.loading) return <Loader />;
|
||||
|
||||
|
@ -59,21 +64,21 @@ export const ShowHistory = React.memo(function ShowHistory(props: ShowHistoryPro
|
|||
if (!historyData) return null;
|
||||
|
||||
return (
|
||||
<div className="show-history">
|
||||
<div className="supervisor-history-panel">
|
||||
<Tabs animate renderActiveTabPanelOnly vertical defaultSelectedTabId={0}>
|
||||
{historyData.map((pastSupervisor, i) => (
|
||||
<Tab
|
||||
id={i}
|
||||
key={i}
|
||||
title={pastSupervisor.version}
|
||||
panelClassName="panel"
|
||||
panel={
|
||||
<ShowValue
|
||||
jsonValue={JSONBig.stringify(pastSupervisor.spec, undefined, 2)}
|
||||
onDiffWithPrevious={i < historyData.length - 1 ? () => setDiffIndex(i) : undefined}
|
||||
downloadFilename={`${downloadFilenamePrefix}-version-${pastSupervisor.version}.json`}
|
||||
downloadFilename={`supervisor-${supervisorId}-version-${pastSupervisor.version}.json`}
|
||||
/>
|
||||
}
|
||||
panelClassName="panel"
|
||||
/>
|
||||
))}
|
||||
<Tabs.Expander />
|
|
@ -1,9 +1,9 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CompactionDialog matches snapshot with compactionConfig (dynamic partitionsSpec) 1`] = `
|
||||
exports[`CompactionConfigDialog matches snapshot with compactionConfig (dynamic partitionsSpec) 1`] = `
|
||||
<Blueprint4.Dialog
|
||||
canOutsideClickClose={false}
|
||||
className="compaction-dialog"
|
||||
className="compaction-config-dialog"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
title="Compaction config: test1"
|
||||
|
@ -356,6 +356,12 @@ exports[`CompactionDialog matches snapshot with compactionConfig (dynamic partit
|
|||
<div
|
||||
className="bp4-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint4.Button
|
||||
className="history-button"
|
||||
minimal={true}
|
||||
onClick={[Function]}
|
||||
text="History"
|
||||
/>
|
||||
<Blueprint4.Button
|
||||
intent="danger"
|
||||
onClick={[Function]}
|
||||
|
@ -376,10 +382,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (dynamic partit
|
|||
</Blueprint4.Dialog>
|
||||
`;
|
||||
|
||||
exports[`CompactionDialog matches snapshot with compactionConfig (hashed partitionsSpec) 1`] = `
|
||||
exports[`CompactionConfigDialog matches snapshot with compactionConfig (hashed partitionsSpec) 1`] = `
|
||||
<Blueprint4.Dialog
|
||||
canOutsideClickClose={false}
|
||||
className="compaction-dialog"
|
||||
className="compaction-config-dialog"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
title="Compaction config: test1"
|
||||
|
@ -732,6 +738,12 @@ exports[`CompactionDialog matches snapshot with compactionConfig (hashed partiti
|
|||
<div
|
||||
className="bp4-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint4.Button
|
||||
className="history-button"
|
||||
minimal={true}
|
||||
onClick={[Function]}
|
||||
text="History"
|
||||
/>
|
||||
<Blueprint4.Button
|
||||
intent="danger"
|
||||
onClick={[Function]}
|
||||
|
@ -752,10 +764,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (hashed partiti
|
|||
</Blueprint4.Dialog>
|
||||
`;
|
||||
|
||||
exports[`CompactionDialog matches snapshot with compactionConfig (range partitionsSpec) 1`] = `
|
||||
exports[`CompactionConfigDialog matches snapshot with compactionConfig (range partitionsSpec) 1`] = `
|
||||
<Blueprint4.Dialog
|
||||
canOutsideClickClose={false}
|
||||
className="compaction-dialog"
|
||||
className="compaction-config-dialog"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
title="Compaction config: test1"
|
||||
|
@ -1108,6 +1120,12 @@ exports[`CompactionDialog matches snapshot with compactionConfig (range partitio
|
|||
<div
|
||||
className="bp4-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint4.Button
|
||||
className="history-button"
|
||||
minimal={true}
|
||||
onClick={[Function]}
|
||||
text="History"
|
||||
/>
|
||||
<Blueprint4.Button
|
||||
intent="danger"
|
||||
onClick={[Function]}
|
||||
|
@ -1128,10 +1146,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (range partitio
|
|||
</Blueprint4.Dialog>
|
||||
`;
|
||||
|
||||
exports[`CompactionDialog matches snapshot without compactionConfig 1`] = `
|
||||
exports[`CompactionConfigDialog matches snapshot without compactionConfig 1`] = `
|
||||
<Blueprint4.Dialog
|
||||
canOutsideClickClose={false}
|
||||
className="compaction-dialog"
|
||||
className="compaction-config-dialog"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
title="Compaction config: test1"
|
||||
|
@ -1484,6 +1502,12 @@ exports[`CompactionDialog matches snapshot without compactionConfig 1`] = `
|
|||
<div
|
||||
className="bp4-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint4.Button
|
||||
className="history-button"
|
||||
minimal={true}
|
||||
onClick={[Function]}
|
||||
text="History"
|
||||
/>
|
||||
<Blueprint4.Button
|
||||
onClick={[Function]}
|
||||
text="Close"
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
@import '../../variables';
|
||||
|
||||
.compaction-dialog {
|
||||
.compaction-config-dialog {
|
||||
&.#{$bp-ns}-dialog {
|
||||
height: 80vh;
|
||||
}
|
||||
|
@ -32,6 +32,17 @@
|
|||
margin: 15px;
|
||||
}
|
||||
|
||||
.#{$bp-ns}-dialog-footer-actions {
|
||||
position: relative;
|
||||
|
||||
.history-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 0 15px 10px 0;
|
||||
padding: 0 5px 0 15px;
|
|
@ -20,12 +20,12 @@ import React from 'react';
|
|||
|
||||
import { shallow } from '../../utils/shallow-renderer';
|
||||
|
||||
import { CompactionDialog } from './compaction-dialog';
|
||||
import { CompactionConfigDialog } from './compaction-config-dialog';
|
||||
|
||||
describe('CompactionDialog', () => {
|
||||
describe('CompactionConfigDialog', () => {
|
||||
it('matches snapshot without compactionConfig', () => {
|
||||
const compactionDialog = shallow(
|
||||
<CompactionDialog
|
||||
<CompactionConfigDialog
|
||||
onClose={() => {}}
|
||||
onSave={() => {}}
|
||||
onDelete={() => {}}
|
||||
|
@ -38,7 +38,7 @@ describe('CompactionDialog', () => {
|
|||
|
||||
it('matches snapshot with compactionConfig (dynamic partitionsSpec)', () => {
|
||||
const compactionDialog = shallow(
|
||||
<CompactionDialog
|
||||
<CompactionConfigDialog
|
||||
onClose={() => {}}
|
||||
onSave={() => {}}
|
||||
onDelete={() => {}}
|
||||
|
@ -54,7 +54,7 @@ describe('CompactionDialog', () => {
|
|||
|
||||
it('matches snapshot with compactionConfig (hashed partitionsSpec)', () => {
|
||||
const compactionDialog = shallow(
|
||||
<CompactionDialog
|
||||
<CompactionConfigDialog
|
||||
onClose={() => {}}
|
||||
onSave={() => {}}
|
||||
onDelete={() => {}}
|
||||
|
@ -70,7 +70,7 @@ describe('CompactionDialog', () => {
|
|||
|
||||
it('matches snapshot with compactionConfig (range partitionsSpec)', () => {
|
||||
const compactionDialog = shallow(
|
||||
<CompactionDialog
|
||||
<CompactionConfigDialog
|
||||
onClose={() => {}}
|
||||
onSave={() => {}}
|
||||
onDelete={() => {}}
|
|
@ -27,10 +27,11 @@ import {
|
|||
compactionConfigHasLegacyInputSegmentSizeBytesSet,
|
||||
} from '../../druid-models';
|
||||
import { deepDelete, formatBytesCompact } from '../../utils';
|
||||
import { CompactionHistoryDialog } from '../compaction-history-dialog/compaction-history-dialog';
|
||||
|
||||
import './compaction-dialog.scss';
|
||||
import './compaction-config-dialog.scss';
|
||||
|
||||
export interface CompactionDialogProps {
|
||||
export interface CompactionConfigDialogProps {
|
||||
onClose: () => void;
|
||||
onSave: (compactionConfig: CompactionConfig) => void | Promise<void>;
|
||||
onDelete: () => void;
|
||||
|
@ -38,9 +39,12 @@ export interface CompactionDialogProps {
|
|||
compactionConfig: CompactionConfig | undefined;
|
||||
}
|
||||
|
||||
export const CompactionDialog = React.memo(function CompactionDialog(props: CompactionDialogProps) {
|
||||
export const CompactionConfigDialog = React.memo(function CompactionConfigDialog(
|
||||
props: CompactionConfigDialogProps,
|
||||
) {
|
||||
const { datasource, compactionConfig, onSave, onClose, onDelete } = props;
|
||||
|
||||
const [showHistory, setShowHistory] = useState(false);
|
||||
const [currentTab, setCurrentTab] = useState<FormJsonTabs>('form');
|
||||
const [currentConfig, setCurrentConfig] = useState<CompactionConfig>(
|
||||
compactionConfig || {
|
||||
|
@ -53,9 +57,15 @@ export const CompactionDialog = React.memo(function CompactionDialog(props: Comp
|
|||
const issueWithCurrentConfig = AutoForm.issueWithModel(currentConfig, COMPACTION_CONFIG_FIELDS);
|
||||
const disableSubmit = Boolean(jsonError || issueWithCurrentConfig);
|
||||
|
||||
if (showHistory) {
|
||||
return (
|
||||
<CompactionHistoryDialog datasource={datasource} onClose={() => setShowHistory(false)} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
className="compaction-dialog"
|
||||
className="compaction-config-dialog"
|
||||
isOpen
|
||||
onClose={onClose}
|
||||
canOutsideClickClose={false}
|
||||
|
@ -100,6 +110,12 @@ export const CompactionDialog = React.memo(function CompactionDialog(props: Comp
|
|||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button
|
||||
className="history-button"
|
||||
text="History"
|
||||
minimal
|
||||
onClick={() => setShowHistory(true)}
|
||||
/>
|
||||
{compactionConfig && <Button text="Delete" intent={Intent.DANGER} onClick={onDelete} />}
|
||||
<Button text="Close" onClick={onClose} />
|
||||
<Button
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@import '../../variables';
|
||||
|
||||
.compaction-history-dialog {
|
||||
&.#{$bp-ns}-dialog {
|
||||
width: 70vw;
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.#{$bp-ns}-tabs {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.global-info {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 30px;
|
||||
right: 10px;
|
||||
width: auto;
|
||||
white-space: pre;
|
||||
background: $gray1;
|
||||
}
|
||||
}
|
||||
|
||||
.loader {
|
||||
position: relative;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Callout, Classes, Code, Dialog, Tab, Tabs } from '@blueprintjs/core';
|
||||
import * as JSONBig from 'json-bigint-native';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Loader, ShowValue } from '../../components';
|
||||
import type { CompactionConfig } from '../../druid-models';
|
||||
import { useQueryManager } from '../../hooks';
|
||||
import { Api } from '../../singletons';
|
||||
import { formatInteger, formatPercent } from '../../utils';
|
||||
import { DiffDialog } from '../diff-dialog/diff-dialog';
|
||||
|
||||
import './compaction-history-dialog.scss';
|
||||
|
||||
interface CompactionHistoryEntry {
|
||||
auditTime: string;
|
||||
auditInfo: any;
|
||||
globalConfig?: GlobalConfig;
|
||||
compactionConfig: CompactionConfig;
|
||||
}
|
||||
|
||||
interface GlobalConfig {
|
||||
compactionTaskSlotRatio: number;
|
||||
maxCompactionTaskSlots: number;
|
||||
useAutoScaleSlots: boolean;
|
||||
}
|
||||
|
||||
function formatGlobalConfig(globalConfig: GlobalConfig): string {
|
||||
return [
|
||||
`compactionTaskSlotRatio: ${formatPercent(globalConfig.compactionTaskSlotRatio)}`,
|
||||
`maxCompactionTaskSlots: ${formatInteger(globalConfig.maxCompactionTaskSlots)}`,
|
||||
`useAutoScaleSlots: ${globalConfig.useAutoScaleSlots}`,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
export interface CompactionHistoryDialogProps {
|
||||
datasource: string;
|
||||
onClose(): void;
|
||||
}
|
||||
|
||||
export const CompactionHistoryDialog = React.memo(function CompactionHistoryDialog(
|
||||
props: CompactionHistoryDialogProps,
|
||||
) {
|
||||
const { datasource, onClose } = props;
|
||||
|
||||
const [diffIndex, setDiffIndex] = useState(-1);
|
||||
const [historyState] = useQueryManager<string, CompactionHistoryEntry[]>({
|
||||
initQuery: datasource,
|
||||
processQuery: async datasource => {
|
||||
try {
|
||||
const resp = await Api.instance.get(
|
||||
`/druid/coordinator/v1/config/compaction/${Api.encodePath(datasource)}/history?count=20`,
|
||||
);
|
||||
return resp.data;
|
||||
} catch (e) {
|
||||
if (e.response?.status === 404) return [];
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const historyData = historyState.data;
|
||||
return (
|
||||
<Dialog
|
||||
className="compaction-history-dialog"
|
||||
isOpen
|
||||
onClose={onClose}
|
||||
canOutsideClickClose={false}
|
||||
title={`Compaction history: ${datasource}`}
|
||||
>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{historyData ? (
|
||||
historyData.length ? (
|
||||
<Tabs animate renderActiveTabPanelOnly vertical defaultSelectedTabId={0}>
|
||||
{historyData.map((historyEntry, i) => (
|
||||
<Tab
|
||||
id={i}
|
||||
key={i}
|
||||
title={historyEntry.auditTime}
|
||||
panelClassName="panel"
|
||||
panel={
|
||||
<>
|
||||
<ShowValue
|
||||
jsonValue={JSONBig.stringify(historyEntry.compactionConfig, undefined, 2)}
|
||||
onDiffWithPrevious={
|
||||
i < historyData.length - 1 ? () => setDiffIndex(i) : undefined
|
||||
}
|
||||
downloadFilename={`compaction-history-${datasource}-version-${historyEntry.auditTime}.json`}
|
||||
/>
|
||||
{historyEntry.globalConfig && (
|
||||
<Callout className="global-info">
|
||||
{formatGlobalConfig(historyEntry.globalConfig)}
|
||||
</Callout>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<Tabs.Expander />
|
||||
</Tabs>
|
||||
) : (
|
||||
<div>
|
||||
There is no compaction history for <Code>{datasource}</Code>.
|
||||
</div>
|
||||
)
|
||||
) : historyState.loading ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<div>{historyState.getErrorMessage()}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button text="Close" onClick={onClose} />
|
||||
</div>
|
||||
</div>
|
||||
{diffIndex !== -1 && historyData && (
|
||||
<DiffDialog
|
||||
title="Compaction config diff"
|
||||
versions={historyData.map(s => ({ label: s.auditTime, value: s.compactionConfig }))}
|
||||
initLeftIndex={diffIndex + 1}
|
||||
initRightIndex={diffIndex}
|
||||
onClose={() => setDiffIndex(-1)}
|
||||
/>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
});
|
|
@ -112,7 +112,7 @@ exports[`HistoryDialog matches snapshot 1`] = `
|
|||
role="tabpanel"
|
||||
>
|
||||
<div
|
||||
class="show-json"
|
||||
class="show-value"
|
||||
>
|
||||
<div
|
||||
class="top-actions"
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
export * from './about-dialog/about-dialog';
|
||||
export * from './async-action-dialog/async-action-dialog';
|
||||
export * from './compaction-dialog/compaction-dialog';
|
||||
export * from './compaction-config-dialog/compaction-config-dialog';
|
||||
export * from './coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog';
|
||||
export * from './diff-dialog/diff-dialog';
|
||||
export * from './doctor-dialog/doctor-dialog';
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { ShowJson } from '../../components';
|
||||
import { ShowHistory } from '../../components/show-history/show-history';
|
||||
import { ShowJson, SupervisorHistoryPanel } from '../../components';
|
||||
import { cleanSpec } from '../../druid-models';
|
||||
import { Api } from '../../singletons';
|
||||
import { deepGet } from '../../utils';
|
||||
|
@ -96,12 +95,7 @@ export const SupervisorTableActionDialog = React.memo(function SupervisorTableAc
|
|||
downloadFilename={`supervisor-payload-${supervisorId}.json`}
|
||||
/>
|
||||
)}
|
||||
{activeTab === 'history' && (
|
||||
<ShowHistory
|
||||
endpoint={`${supervisorEndpointBase}/history`}
|
||||
downloadFilenamePrefix={`supervisor-${supervisorId}`}
|
||||
/>
|
||||
)}
|
||||
{activeTab === 'history' && <SupervisorHistoryPanel supervisorId={supervisorId} />}
|
||||
</TableActionDialog>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { dedupe } from 'druid-query-toolkit';
|
||||
import * as JSONBig from 'json-bigint-native';
|
||||
|
||||
import type {
|
||||
|
@ -91,18 +92,6 @@ export interface ExampleManifest {
|
|||
spec: any;
|
||||
}
|
||||
|
||||
function dedupe(xs: string[]): string[] {
|
||||
const seen: Record<string, boolean> = {};
|
||||
return xs.filter(x => {
|
||||
if (seen[x]) {
|
||||
return false;
|
||||
} else {
|
||||
seen[x] = true;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getCacheRowsFromSampleResponse(sampleResponse: SampleResponse): CacheRows {
|
||||
return filterMap(sampleResponse.data, d => d.input).slice(0, 20);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ import {
|
|||
} from '../../components';
|
||||
import {
|
||||
AsyncActionDialog,
|
||||
CompactionDialog,
|
||||
CompactionConfigDialog,
|
||||
KillDatasourceDialog,
|
||||
RetentionDialog,
|
||||
} from '../../dialogs';
|
||||
|
@ -233,7 +233,7 @@ interface RetentionDialogOpenOn {
|
|||
readonly rules: Rule[];
|
||||
}
|
||||
|
||||
interface CompactionDialogOpenOn {
|
||||
interface CompactionConfigDialogOpenOn {
|
||||
readonly datasource: string;
|
||||
readonly compactionConfig?: CompactionConfig;
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ export interface DatasourcesViewState {
|
|||
|
||||
showUnused: boolean;
|
||||
retentionDialogOpenOn?: RetentionDialogOpenOn;
|
||||
compactionDialogOpenOn?: CompactionDialogOpenOn;
|
||||
compactionDialogOpenOn?: CompactionConfigDialogOpenOn;
|
||||
datasourceToMarkAsUnusedAllSegmentsIn?: string;
|
||||
datasourceToMarkAllNonOvershadowedSegmentsAsUsedIn?: string;
|
||||
killDatasource?: string;
|
||||
|
@ -981,12 +981,12 @@ ORDER BY 1`;
|
|||
);
|
||||
}
|
||||
|
||||
private renderCompactionDialog() {
|
||||
private renderCompactionConfigDialog() {
|
||||
const { datasourcesAndDefaultRulesState, compactionDialogOpenOn } = this.state;
|
||||
if (!compactionDialogOpenOn || !datasourcesAndDefaultRulesState.data) return;
|
||||
|
||||
return (
|
||||
<CompactionDialog
|
||||
<CompactionConfigDialog
|
||||
datasource={compactionDialogOpenOn.datasource}
|
||||
compactionConfig={compactionDialogOpenOn.compactionConfig}
|
||||
onClose={() => this.setState({ compactionDialogOpenOn: undefined })}
|
||||
|
@ -1571,7 +1571,7 @@ ORDER BY 1`;
|
|||
{this.renderUseUnuseActionByInterval()}
|
||||
{this.renderKillAction()}
|
||||
{this.renderRetentionDialog()}
|
||||
{this.renderCompactionDialog()}
|
||||
{this.renderCompactionConfigDialog()}
|
||||
{this.renderForceCompactAction()}
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue