Skip to content

Commit

Permalink
Add pipeline edit modal with parallelism control
Browse files Browse the repository at this point in the history
Add an "Edit" button on the job details page that opens up a modal. The
modal contains a form that can be used to change the parallelism.

Resolves: #74
  • Loading branch information
jbeisen committed Apr 25, 2023
1 parent f9d5e3c commit b2f9f25
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 8 deletions.
58 changes: 50 additions & 8 deletions arroyo-console/src/routes/pipelines/JobDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Badge,
Box,
Button,
ButtonGroup,
chakra,
Code,
Flex,
Expand Down Expand Up @@ -41,6 +42,7 @@ import {
theme,
Tr,
UnorderedList,
useDisclosure
} from "@chakra-ui/react";
import React, { ReactElement, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
Expand Down Expand Up @@ -76,6 +78,7 @@ import { PipelineGraph } from "./JobGraph";
import { ApiClient } from "../../main";
import { PipelineOutputs } from "./JobOutputs";
import { SqlEditor } from "./SqlEditor";
import PipelineConfigModal from "./PipelineConfigModal";

interface JobDetailState {
pipeline?: JobDetailsResp;
Expand All @@ -95,6 +98,11 @@ export function JobDetail({ client }: { client: ApiClient }) {
const [checkpoints, setCheckpoints] = useState<Array<CheckpointOverview>>([]);
const [checkpointFetchInProgress, setCheckpointFetchInProgress] = useState<boolean>(false);
const [subscribed, setSubscribed] = useState<boolean>(false);
const {
isOpen: configModalOpen,
onOpen: onConfigModalOpen,
onClose: onConfigModalClose
} = useDisclosure();

let { id } = useParams();

Expand Down Expand Up @@ -186,6 +194,15 @@ export function JobDetail({ client }: { client: ApiClient }) {
fetchPipeline();
}

async function updateJobParallelism (parallelism: number) {
console.log(`Setting pipeline parallelism=${parallelism}`);
await (await client()).updateJob({
jobId: id,
parallelism
});
fetchPipeline();
}

let inner;
if (state.pipeline == null || state.pipeline.jobGraph == null) {
inner = <p>Loading</p>;
Expand Down Expand Up @@ -274,7 +291,35 @@ export function JobDetail({ client }: { client: ApiClient }) {
);
}

let configModal = <></>;
if (state.pipeline?.jobGraph?.nodes) {
const { nodes } = state.pipeline.jobGraph;
const parallelism = Math.max(...nodes.map(({ parallelism }) => parallelism));

configModal = (
<PipelineConfigModal
isOpen={configModalOpen}
parallelism={parallelism}
onClose={onConfigModalClose}
updateJobParallelism={updateJobParallelism}
/>
);
}

const editPipelineButton = <Button onClick={onConfigModalOpen}>Edit</Button>;

const actionButton = (
<Button
isDisabled={state.pipeline?.action == undefined}
isLoading={state.pipeline?.inProgress}
loadingText={state.pipeline?.actionText}
onClick={async () => {
await updateJobState(state.pipeline?.action!);
}}
>
{state.pipeline?.actionText}
</Button>
);

return (
<Box top={0} bottom={0} right={0} left={200} position="absolute" overflowY="hidden">
Expand All @@ -286,17 +331,14 @@ export function JobDetail({ client }: { client: ApiClient }) {
</Box>
<Spacer />
<Box p={5}>
<Button
isDisabled={state.pipeline?.action == undefined}
isLoading={state.pipeline?.inProgress}
loadingText={state.pipeline?.actionText}
onClick={() => updateJobState(state.pipeline?.action!)}
>
{state.pipeline?.actionText}
</Button>
<ButtonGroup>
{editPipelineButton}
{actionButton}
</ButtonGroup>
</Box>
</Flex>
{inner}
{configModal}
</Box>
);
}
Expand Down
101 changes: 101 additions & 0 deletions arroyo-console/src/routes/pipelines/PipelineConfigModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { useState } from "react";
import {
Alert,
AlertDescription,
AlertIcon,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from "@chakra-ui/react";

export interface PipelineConfigModalProps {
isOpen: boolean;
parallelism: number;
onClose: () => void;
updateJobParallelism: (parallelism: number) => void;
}

const PipelineConfigModal: React.FC<PipelineConfigModalProps> = ({
isOpen,
parallelism,
onClose,
updateJobParallelism,
}) => {
const [parallelismInputValue, setParallelismInputValue] = useState<number | undefined>(
parallelism
);

const onConfirm = () => {
if (parallelismInputValue) {
updateJobParallelism(parallelismInputValue);
}
onClose();
};

const pipelineRequiresRestart = parallelismInputValue != parallelism;

let changeAlert = <></>;
if (pipelineRequiresRestart) {
changeAlert = (
<Alert status="warning">
<AlertIcon />
<AlertDescription>These changes require a pipeline restart.</AlertDescription>
</Alert>
);
}

const parallelismInput = (
<Input
onChange={event => {
if (!event.target.value) {
setParallelismInputValue(undefined);
}

const parsedNumber = parseInt(event.target.value);
if (!isNaN(parsedNumber)) {
setParallelismInputValue(parsedNumber);
}
}}
value={parallelismInputValue}
/>
);

const form = (
<FormControl isRequired>
<FormLabel>Parallelism</FormLabel>
{parallelismInput}
<FormHelperText>Number of parallel substacks for each node</FormHelperText>
</FormControl>
);

return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Edit Pipeline Configuration</ModalHeader>
<ModalCloseButton />
{changeAlert}
<ModalBody>{form}</ModalBody>
<ModalFooter>
<Button variant="ghost" onClick={onClose}>
Cancel
</Button>
<Button colorScheme="blue" mr={3} onClick={onConfirm}>
Save
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
};

export default PipelineConfigModal;

0 comments on commit b2f9f25

Please sign in to comment.