This is an automated email from the ASF dual-hosted git repository.
jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new 8d061eea14f rebalance ui flow fixes (#16545)
8d061eea14f is described below
commit 8d061eea14f7d717e75a831864b2cb8467348ee8
Author: Saptarshi Sengupta <[email protected]>
AuthorDate: Fri Aug 8 01:07:46 2025 +0530
rebalance ui flow fixes (#16545)
---
.../RebalanceServer/RebalanceResponse.tsx | 24 ++-
.../Operations/RebalanceServerStatusOp.tsx | 57 +++++--
.../Homepage/Operations/RebalanceServerTableOp.tsx | 169 ++++++++++++++-------
3 files changed, 181 insertions(+), 69 deletions(-)
diff --git
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServer/RebalanceResponse.tsx
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServer/RebalanceResponse.tsx
index d8d09a60d6f..67658974100 100644
---
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServer/RebalanceResponse.tsx
+++
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServer/RebalanceResponse.tsx
@@ -54,7 +54,7 @@ const CopyJobIdToClipboardButton = ({ jobId }: { jobId:
string }) => {
</>
);
};
-export const RebalanceResponse = ({ response }) => {
+export const RebalanceResponse = ({ response, onJobIdClick }) => {
const responseSectionsToShow = [
{
name: 'Segment Assignment',
@@ -81,9 +81,25 @@ export const RebalanceResponse = ({ response }) => {
<Grid item xs={6}>
<Typography color='textSecondary'
variant='caption'>Job Id</Typography>
<Box flexDirection="row" display="flex"
alignItems="center" marginTop={-1.25}>
- <Typography style={{ fontWeight: 600 }}
variant='body2'>
- {response.jobId}
- </Typography>
+ {response.status === 'IN_PROGRESS' &&
onJobIdClick ? (
+ <Typography
+ style={{
+ fontWeight: 600,
+ cursor: 'pointer',
+ color: '#1976d2',
+ textDecoration: 'underline'
+ }}
+ variant='body2'
+ onClick={() =>
onJobIdClick(response.jobId)}
+ title="Click to view job details"
+ >
+ {response.jobId}
+ </Typography>
+ ) : (
+ <Typography style={{ fontWeight: 600
}} variant='body2'>
+ {response.jobId}
+ </Typography>
+ )}
<CopyJobIdToClipboardButton
jobId={response.jobId} />
</Box>
</Grid>
diff --git
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerStatusOp.tsx
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerStatusOp.tsx
index 9d61cf6f5ae..4a07b4a0ba9 100644
---
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerStatusOp.tsx
+++
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerStatusOp.tsx
@@ -35,10 +35,11 @@ import {RebalanceTableSegmentJob} from "Models";
type RebalanceServerStatusOpProps = {
tableName: string;
hideModal: () => void;
+ initialJobId?: string;
};
export const RebalanceServerStatusOp = (
- { tableName, hideModal } : RebalanceServerStatusOpProps
+ { tableName, hideModal, initialJobId } : RebalanceServerStatusOpProps
) => {
const { currentTimezone } = useTimezone();
const [rebalanceServerJobs, setRebalanceServerJobs] =
React.useState<RebalanceTableSegmentJob[]>([])
@@ -47,7 +48,7 @@ export const RebalanceServerStatusOp = (
const [rebalanceProgressStats, setRebalanceProgressStats] =
useState<{}>({});
const [loading, setLoading] = useState(false);
- useEffect(() => {
+ const fetchRebalanceJobs = () => {
setLoading(true);
PinotMethodUtils
.fetchRebalanceTableJobs(tableName)
@@ -55,8 +56,35 @@ export const RebalanceServerStatusOp = (
setRebalanceServerJobs(jobs)
})
.finally(() => setLoading(false));
+ };
+
+ useEffect(() => {
+ fetchRebalanceJobs();
}, []);
+ // Set initial job selection when jobs are loaded and initialJobId is
provided
+ useEffect(() => {
+ if (initialJobId && rebalanceServerJobs.length > 0 && !jobSelected) {
+ const jobExists = rebalanceServerJobs.find(job => job.jobId ===
initialJobId);
+ if (jobExists) {
+ setJobSelected(initialJobId);
+ }
+ }
+ }, [initialJobId, rebalanceServerJobs, jobSelected]);
+
+ const RefreshAction = () => {
+ return (
+ <Button
+ variant='outlined'
+ color='primary'
+ onClick={fetchRebalanceJobs}
+ disabled={loading}
+ >
+ Refresh
+ </Button>
+ );
+ };
+
const BackAction = () => {
return (
<Button
@@ -67,7 +95,8 @@ export const RebalanceServerStatusOp = (
Back
</Button>
);
- }
+ };
+
useEffect(() => {
try {
@@ -107,10 +136,15 @@ export const RebalanceServerStatusOp = (
<Dialog
open={true}
handleClose={hideModal}
- title="Rebalance Table Status"
+ title="Rebalance Servers Status"
showOkBtn={false}
size='lg'
- moreActions={jobSelected ? <BackAction /> : null}
+ moreActions={
+ <Box display="flex">
+ <RefreshAction />
+ {jobSelected && !initialJobId && <Box
ml={1}><BackAction /></Box>}
+ </Box>
+ }
>
<DialogContent>
<Box alignItems='center' display='flex'
justifyContent='center'>
@@ -125,10 +159,15 @@ export const RebalanceServerStatusOp = (
<Dialog
open={true}
handleClose={hideModal}
- title="Rebalance Table Status"
+ title="Rebalance Servers Status"
showOkBtn={false}
size='lg'
- moreActions={jobSelected ? <BackAction /> : null}
+ moreActions={
+ <Box display="flex">
+ <RefreshAction />
+ {jobSelected && !initialJobId && <Box ml={1}><BackAction
/></Box>}
+ </Box>
+ }
>
<DialogContent>
{
@@ -147,10 +186,10 @@ export const RebalanceServerStatusOp = (
rebalanceServerJob.jobId,
rebalanceServerJob.tableName,
progressStats.status,
-
formatTimeInTimezone(+rebalanceServerJob.submissionTimeMs)
+
formatTimeInTimezone(+rebalanceServerJob.submissionTimeMs +
(JSON.parse(rebalanceServerJob?.REBALANCE_PROGRESS_STATS ||
'{}').timeToFinishInSeconds * 1000))
];
}),
- columns: ['Job id', 'Table name', 'Status',
'Started at']
+ columns: ['Job id', 'Table name', 'Status',
'Started at', 'Finished at']
}}
showSearchBox
/> :
diff --git
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerTableOp.tsx
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerTableOp.tsx
index dd752bfa1e3..4a9773394c3 100644
---
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerTableOp.tsx
+++
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerTableOp.tsx
@@ -32,6 +32,7 @@ import InfoOutlinedIcon from
"@material-ui/icons/InfoOutlined";
import {rebalanceServerOptions} from
"./RebalanceServer/RebalanceServerOptions";
import {RebalanceServerConfigurationOption} from
"./RebalanceServer/RebalanceServerConfigurationOption";
import {RebalanceResponse} from "./RebalanceServer/RebalanceResponse";
+import {RebalanceServerStatusOp} from "./RebalanceServerStatusOp";
type Props = {
tableType: string,
@@ -41,12 +42,20 @@ type Props = {
const DryRunAction = ({ handleOnRun, disabled }: { handleOnRun: () => void,
disabled?: boolean }) => {
return (
- <Button disabled={disabled} onClick={handleOnRun} variant="outlined"
style={{ textTransform: 'none' }} color="primary">
+ <Button disabled={disabled} onClick={handleOnRun} variant="contained"
style={{ textTransform: 'none' }} color="primary">
Dry Run
</Button>
);
}
+const RebalanceAction = ({ handleOnRun, disabled }: { handleOnRun: () => void,
disabled?: boolean }) => {
+ return (
+ <Button disabled={disabled} onClick={handleOnRun} variant="contained"
style={{ textTransform: 'none' }} color="primary">
+ Rebalance
+ </Button>
+ );
+}
+
const BackAction = ({ onClick }: { onClick: () => void }) => {
return (
<Button onClick={onClick} variant="outlined" color="primary">
@@ -55,6 +64,8 @@ const BackAction = ({ onClick }: { onClick: () => void }) => {
);
}
+
+
export default function RebalanceServerTableOp({
hideModal,
tableName,
@@ -65,6 +76,12 @@ export default function RebalanceServerTableOp({
const [rebalanceConfig, setRebalanceConfig] = React.useState(
rebalanceServerOptions.reduce((config, option) => ({ ...config,
[option.name]: option.defaultValue }), {})
);
+ const [isDryRun, setIsDryRun] = React.useState(false);
+ const [dryRunCompleted, setDryRunCompleted] = React.useState(false);
+ const [dryRunResponse, setDryRunResponse] = React.useState(null);
+ const [showingDryRunResults, setShowingDryRunResults] =
React.useState(false);
+ const [showJobStatusDialog, setShowJobStatusDialog] = React.useState(false);
+ const [selectedJobId, setSelectedJobId] = React.useState<string |
null>(null);
const getData = () => {
return {
@@ -87,10 +104,14 @@ export default function RebalanceServerTableOp({
} else {
setRebalanceResponse(response);
}
+
+ setShowingDryRunResults(false);
+ setIsDryRun(false);
setPending(false);
};
- const handleDryRun = async () => {
+ const handleDryRun = async () => {
+ setIsDryRun(true);
const data = getData();
setPending(true);
const response = await
PinotMethodUtils.rebalanceServersForTableOp(tableName, {
@@ -106,7 +127,10 @@ export default function RebalanceServerTableOp({
})
} else {
setRebalanceResponse(response);
+ setDryRunResponse(response);
+ setDryRunCompleted(true);
}
+ setShowingDryRunResults(true);
setPending(false);
};
@@ -137,62 +161,95 @@ export default function RebalanceServerTableOp({
const handleBackBtnOnClick = () => {
setRebalanceResponse(null);
+ setShowingDryRunResults(false);
+ setIsDryRun(false);
}
- return (
- <Dialog
- showTitleDivider
- showFooterDivider
- size='md'
- okBtnDisabled={pending}
- open={true}
- handleClose={hideModal}
- title={<RebalanceServerDialogHeader />}
- handleSave={handleSave}
- btnOkText='Rebalance'
- showOkBtn={!rebalanceResponse}
- moreActions={
- !rebalanceResponse ?
- <DryRunAction disabled={pending} handleOnRun={handleDryRun} /> :
- <BackAction onClick={handleBackBtnOnClick} />
- }
- >
- {!rebalanceResponse ?
- <Box flexDirection="column">
- <RebalanceServerSection sectionTitle='Before you begin'>
- <Alert color='info' icon={<InfoOutlinedIcon fontSize='small' />}>
- <Typography variant='body2'>
- It is strongly recommended to run once via "Dry Run" with
the options enabled prior to running the actual "Rebalance" operation.
- This is needed to verify that rebalance will do what's
expected.
- </Typography>
- </Alert>
- </RebalanceServerSection>
- <Divider style={{ marginBottom: 20 }} />
- <RebalanceServerSection sectionTitle='Basic Options'>
- <Grid container spacing={2}>
- {rebalanceServerOptions.filter(option =>
!option.isAdvancedConfig && !option.isStatsGatheringConfig).map((option) => (
- <Grid item xs={12} key={`basic-options-${option.name}`}>
- <RebalanceServerConfigurationOption
rebalanceConfig={rebalanceConfig} option={option}
handleConfigChange={handleConfigChange} />
- </Grid>
- ))}
- </Grid>
- </RebalanceServerSection>
- <Divider style={{ marginBottom: 20 }}/>
- <RebalanceServerSection sectionTitle='Advanced Options'
canHideSection showSectionByDefault={false}>
- <Grid container spacing={2}>
- {rebalanceServerOptions.filter(option =>
option.isAdvancedConfig).map((option) => (
- <Grid item xs={12} key={`advanced-options-${option.name}`}>
- <RebalanceServerConfigurationOption
rebalanceConfig={rebalanceConfig} option={option}
handleConfigChange={handleConfigChange} />
- </Grid>
- ))}
- </Grid>
- </RebalanceServerSection>
+ const handleJobIdClick = (jobId: string) => {
+ setSelectedJobId(jobId);
+ setShowJobStatusDialog(true);
+ }
+
+ const handleCloseJobStatusDialog = () => {
+ setShowJobStatusDialog(false);
+ setSelectedJobId(null);
+ }
+
+ const getDialogActions = () => {
+ if (!showingDryRunResults && !rebalanceResponse) {
+ return <DryRunAction disabled={pending} handleOnRun={handleDryRun} />;
+ }
+
+ if (showingDryRunResults) {
+ return (
+ <Box display="flex">
+ <BackAction onClick={handleBackBtnOnClick} />
+ <Box ml={1}>
+ <RebalanceAction disabled={pending} handleOnRun={handleSave} />
</Box>
- :
- <React.Fragment>
- <RebalanceResponse response={rebalanceResponse} />
- </React.Fragment>
- }
- </Dialog>
+ </Box>
+ );
+ }
+
+ return <BackAction onClick={handleBackBtnOnClick} />;
+ };
+
+ return (
+ <React.Fragment>
+ <Dialog
+ showTitleDivider
+ showFooterDivider
+ size='md'
+ okBtnDisabled={pending}
+ open={true}
+ handleClose={hideModal}
+ title={<RebalanceServerDialogHeader />}
+ showOkBtn={false}
+ moreActions={getDialogActions()}
+ >
+ {!showingDryRunResults && !rebalanceResponse && (
+ <Box flexDirection="column">
+ <RebalanceServerSection sectionTitle='Basic Options'>
+ <Grid container spacing={2}>
+ {rebalanceServerOptions.filter(option =>
!option.isAdvancedConfig && !option.isStatsGatheringConfig).map((option) => (
+ <Grid item xs={12} key={`basic-options-${option.name}`}>
+ <RebalanceServerConfigurationOption
rebalanceConfig={rebalanceConfig} option={option}
handleConfigChange={handleConfigChange} />
+ </Grid>
+ ))}
+ </Grid>
+ </RebalanceServerSection>
+ <Divider style={{ marginBottom: 20 }}/>
+ <RebalanceServerSection sectionTitle='Advanced Options'
canHideSection showSectionByDefault={false}>
+ <Grid container spacing={2}>
+ {rebalanceServerOptions.filter(option =>
option.isAdvancedConfig).map((option) => (
+ <Grid item xs={12}
key={`advanced-options-${option.name}`}>
+ <RebalanceServerConfigurationOption
rebalanceConfig={rebalanceConfig} option={option}
handleConfigChange={handleConfigChange} />
+ </Grid>
+ ))}
+ </Grid>
+ </RebalanceServerSection>
+ </Box>
+ )}
+
+ {showingDryRunResults && dryRunResponse && (
+ <React.Fragment>
+ <RebalanceResponse response={dryRunResponse}
onJobIdClick={handleJobIdClick} />
+ </React.Fragment>
+ )}
+
+ {rebalanceResponse && !showingDryRunResults && (
+ <React.Fragment>
+ <RebalanceResponse response={rebalanceResponse}
onJobIdClick={handleJobIdClick} />
+ </React.Fragment>
+ )}
+ </Dialog>
+ {showJobStatusDialog && (
+ <RebalanceServerStatusOp
+ tableName={tableName}
+ hideModal={handleCloseJobStatusDialog}
+ initialJobId={selectedJobId}
+ />
+ )}
+ </React.Fragment>
);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]