mirror of https://github.com/apache/druid.git
Web console: Reset to specific offsets dialog (#14863)
* add dialog * copy changes
This commit is contained in:
parent
59415ba9b2
commit
7e147ee905
|
@ -34,6 +34,7 @@ export * from './retention-dialog/retention-dialog';
|
|||
export * from './snitch-dialog/snitch-dialog';
|
||||
export * from './spec-dialog/spec-dialog';
|
||||
export * from './string-input-dialog/string-input-dialog';
|
||||
export * from './supervisor-reset-offsets-dialog/supervisor-reset-offsets-dialog';
|
||||
export * from './supervisor-table-action-dialog/supervisor-table-action-dialog';
|
||||
export * from './table-action-dialog/table-action-dialog';
|
||||
export * from './task-table-action-dialog/task-table-action-dialog';
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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, Classes, Code, ControlGroup, Dialog, FormGroup, Intent } from '@blueprintjs/core';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { FancyNumericInput } from '../../components/fancy-numeric-input/fancy-numeric-input';
|
||||
import { useQueryManager } from '../../hooks';
|
||||
import { Api, AppToaster } from '../../singletons';
|
||||
import { deepGet, getDruidErrorMessage } from '../../utils';
|
||||
|
||||
type OffsetMap = Record<string, number>;
|
||||
|
||||
interface SupervisorResetOffsetsDialogProps {
|
||||
supervisorId: string;
|
||||
supervisorType: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const SupervisorResetOffsetsDialog = React.memo(function SupervisorResetOffsetsDialog(
|
||||
props: SupervisorResetOffsetsDialogProps,
|
||||
) {
|
||||
const { supervisorId, supervisorType, onClose } = props;
|
||||
const [offsetsToResetTo, setOffsetsToResetTo] = useState<OffsetMap>({});
|
||||
|
||||
const [statusResp] = useQueryManager<string, OffsetMap>({
|
||||
initQuery: supervisorId,
|
||||
processQuery: async supervisorId => {
|
||||
const statusResp = await Api.instance.get(
|
||||
`/druid/indexer/v1/supervisor/${Api.encodePath(supervisorId)}/status`,
|
||||
);
|
||||
return statusResp.data;
|
||||
},
|
||||
});
|
||||
|
||||
const stream = deepGet(statusResp.data || {}, 'payload.stream');
|
||||
const latestOffsets = deepGet(statusResp.data || {}, 'payload.latestOffsets');
|
||||
|
||||
async function onSave() {
|
||||
if (!stream) return;
|
||||
if (!Object.keys(offsetsToResetTo).length) return;
|
||||
|
||||
try {
|
||||
await Api.instance.post(
|
||||
`/druid/indexer/v1/supervisor/${Api.encodePath(supervisorId)}/resetOffsets`,
|
||||
{
|
||||
type: supervisorType,
|
||||
partitions: {
|
||||
type: 'end',
|
||||
stream,
|
||||
partitionOffsetMap: offsetsToResetTo,
|
||||
},
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
AppToaster.show({
|
||||
message: `Failed to set offsets: ${getDruidErrorMessage(e)}`,
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
AppToaster.show({
|
||||
message: `${supervisorId} offsets have been set`,
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
onClose();
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
className="supervisor-reset-offsets-dialog"
|
||||
isOpen
|
||||
onClose={onClose}
|
||||
title={`Set supervisor offsets: ${supervisorId}`}
|
||||
>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<p>
|
||||
Set <Code>{supervisorId}</Code> to specific offsets
|
||||
</p>
|
||||
{latestOffsets &&
|
||||
Object.entries(latestOffsets).map(([key, latestOffset]) => (
|
||||
<FormGroup key={key}>
|
||||
<ControlGroup>
|
||||
<Button text={`${key} (currently: ${latestOffset})`} disabled />
|
||||
<FancyNumericInput
|
||||
value={offsetsToResetTo[key]}
|
||||
onValueChange={valueAsNumber => {
|
||||
setOffsetsToResetTo({ ...offsetsToResetTo, [key]: valueAsNumber });
|
||||
}}
|
||||
min={0}
|
||||
fill
|
||||
placeholder={"Don't change"}
|
||||
/>
|
||||
</ControlGroup>
|
||||
</FormGroup>
|
||||
))}
|
||||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button text="Close" onClick={onClose} />
|
||||
<Button text="Save" intent={Intent.PRIMARY} onClick={() => void onSave()} />
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
});
|
|
@ -40,6 +40,7 @@ import {
|
|||
SpecDialog,
|
||||
SupervisorTableActionDialog,
|
||||
} from '../../dialogs';
|
||||
import { SupervisorResetOffsetsDialog } from '../../dialogs/supervisor-reset-offsets-dialog/supervisor-reset-offsets-dialog';
|
||||
import type { QueryWithContext } from '../../druid-models';
|
||||
import type { Capabilities } from '../../helpers';
|
||||
import { SMALL_TABLE_PAGE_SIZE, SMALL_TABLE_PAGE_SIZE_OPTIONS } from '../../react-table';
|
||||
|
@ -108,6 +109,7 @@ export interface SupervisorsViewState {
|
|||
|
||||
resumeSupervisorId?: string;
|
||||
suspendSupervisorId?: string;
|
||||
resetOffsetsSupervisorInfo?: { id: string; type: string };
|
||||
resetSupervisorId?: string;
|
||||
terminateSupervisorId?: string;
|
||||
|
||||
|
@ -339,6 +341,11 @@ GROUP BY 1, 2`;
|
|||
? this.setState({ resumeSupervisorId: id })
|
||||
: this.setState({ suspendSupervisorId: id }),
|
||||
},
|
||||
{
|
||||
icon: IconNames.STEP_BACKWARD,
|
||||
title: 'Set offsets',
|
||||
onAction: () => this.setState({ resetOffsetsSupervisorInfo: { id, type } }),
|
||||
},
|
||||
{
|
||||
icon: IconNames.STEP_BACKWARD,
|
||||
title: 'Hard reset',
|
||||
|
@ -417,6 +424,21 @@ GROUP BY 1, 2`;
|
|||
);
|
||||
}
|
||||
|
||||
renderResetOffsetsSupervisorAction() {
|
||||
const { resetOffsetsSupervisorInfo } = this.state;
|
||||
if (!resetOffsetsSupervisorInfo) return;
|
||||
|
||||
return (
|
||||
<SupervisorResetOffsetsDialog
|
||||
supervisorId={resetOffsetsSupervisorInfo.id}
|
||||
supervisorType={resetOffsetsSupervisorInfo.type}
|
||||
onClose={() => {
|
||||
this.setState({ resetOffsetsSupervisorInfo: undefined });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderResetSupervisorAction() {
|
||||
const { resetSupervisorId } = this.state;
|
||||
if (!resetSupervisorId) return;
|
||||
|
@ -426,7 +448,7 @@ GROUP BY 1, 2`;
|
|||
action={async () => {
|
||||
const resp = await Api.instance.post(
|
||||
`/druid/indexer/v1/supervisor/${Api.encodePath(resetSupervisorId)}/reset`,
|
||||
{},
|
||||
'',
|
||||
);
|
||||
return resp.data;
|
||||
}}
|
||||
|
@ -784,6 +806,7 @@ GROUP BY 1, 2`;
|
|||
{this.renderSupervisorTable()}
|
||||
{this.renderResumeSupervisorAction()}
|
||||
{this.renderSuspendSupervisorAction()}
|
||||
{this.renderResetOffsetsSupervisorAction()}
|
||||
{this.renderResetSupervisorAction()}
|
||||
{this.renderTerminateSupervisorAction()}
|
||||
{supervisorSpecDialogOpen && (
|
||||
|
|
Loading…
Reference in New Issue