This is an automated email from the ASF dual-hosted git repository.

agrove pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-ballista.git


The following commit(s) were added to refs/heads/master by this push:
     new 42e9617d Show job stages metrics (#323)
42e9617d is described below

commit 42e9617de690695965387eb0f1bf36ab061effb1
Author: Stefan Stanciulescu 
<[email protected]>
AuthorDate: Fri Oct 7 05:27:31 2022 -0700

    Show job stages metrics (#323)
    
    * Add support for viewing stage metrics for a job by clicking on the job id
    
    * Cleanup
    
    * Formatting and cleanup
---
 .../scheduler/src/components/JobStagesMetrics.tsx  | 92 ++++++++++++++++++++++
 .../ui/scheduler/src/components/QueriesList.tsx    | 64 +++++++++++++--
 2 files changed, 150 insertions(+), 6 deletions(-)

diff --git a/ballista/ui/scheduler/src/components/JobStagesMetrics.tsx 
b/ballista/ui/scheduler/src/components/JobStagesMetrics.tsx
new file mode 100644
index 00000000..84dc7e14
--- /dev/null
+++ b/ballista/ui/scheduler/src/components/JobStagesMetrics.tsx
@@ -0,0 +1,92 @@
+// 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 { Skeleton, Box } from "@chakra-ui/react";
+import { Column, DataTable } from "./DataTable";
+
+export enum StageStatus {
+  QUEUED = "QUEUED",
+  RUNNING = "RUNNING",
+  FAILED = "FAILED",
+  COMPLETED = "COMPLETED",
+}
+
+export interface Stage {
+  stage_id: string;
+  stage_status: StageStatus;
+  input_rows: number;
+  output_rows: number;
+  elapsed_compute: string;
+}
+
+export interface StagesListProps {
+  stages?: Stage[];
+}
+
+const columns: Column<any>[] = [
+  {
+    Header: "Stage ID",
+    accessor: "stage_id",
+  },
+  {
+    Header: "Status",
+    accessor: "stage_status",
+  },
+  {
+    Header: "Input Rows",
+    accessor: "input_rows",
+  },
+  {
+    Header: "Output Rows",
+    accessor: "output_rows",
+  },
+  {
+    Header: "Computation time",
+    accessor: "elapsed_compute",
+  },
+];
+
+const getSkeleton = () => (
+  <>
+    <Skeleton height={5} />
+    <Skeleton height={5} />
+    <Skeleton height={5} />
+    <Skeleton height={5} />
+    <Skeleton height={5} />
+  </>
+);
+
+export const JobStagesQueries: React.FunctionComponent<StagesListProps> = ({
+  stages,
+}) => {
+  const isLoaded = typeof stages !== "undefined";
+
+  return (
+    <Box w={"100%"} flex={1}>
+      {isLoaded ? (
+        <DataTable
+          columns={columns}
+          data={stages || []}
+          pageSize={10}
+          pb={10}
+        />
+      ) : (
+        getSkeleton()
+      )}
+    </Box>
+  );
+};
diff --git a/ballista/ui/scheduler/src/components/QueriesList.tsx 
b/ballista/ui/scheduler/src/components/QueriesList.tsx
index 38526147..0fb18b29 100644
--- a/ballista/ui/scheduler/src/components/QueriesList.tsx
+++ b/ballista/ui/scheduler/src/components/QueriesList.tsx
@@ -16,13 +16,11 @@
 // under the License.
 
 import React, { useEffect, useState } from "react";
+import { ExternalLinkIcon } from "@chakra-ui/icons";
 import {
   CircularProgress,
   CircularProgressLabel,
-  VStack,
   Skeleton,
-  Stack,
-  Text,
   Flex,
   Box,
   useDisclosure,
@@ -34,12 +32,14 @@ import {
   ModalFooter,
   ModalHeader,
   ModalOverlay,
+  Link,
 } from "@chakra-ui/react";
-import { Column, DataTable, LinkCell } from "./DataTable";
+import { Column, DataTable } from "./DataTable";
 import { FaStop } from "react-icons/fa";
 import { GrDocumentDownload, GrOverview } from "react-icons/gr";
 import fileDownload from "js-file-download";
 import SVG from "react-inlinesvg";
+import { JobStagesQueries } from "./JobStagesMetrics";
 
 export enum QueryStatus {
   QUEUED = "QUEUED",
@@ -113,7 +113,9 @@ export const ActionsCell: (props: any) => React.ReactNode = 
(props: any) => {
       <Modal isOpen={isOpen} size="small" onClose={onClose}>
         <ModalOverlay />
         <ModalContent>
-          <ModalHeader>Graph for {props.value} job</ModalHeader>
+          <ModalHeader textAlign={"center"}>
+            Graph for {props.value} job
+          </ModalHeader>
           <ModalCloseButton />
           <ModalBody margin="auto">
             <SVG innerRef={ref} src={dot_data} width="auto" />
@@ -129,6 +131,56 @@ export const ActionsCell: (props: any) => React.ReactNode 
= (props: any) => {
   );
 };
 
+export const JobLinkCell: (props: any) => React.ReactNode = (props: any) => {
+  const [stages, setData] = useState();
+  const [loaded, setLoaded] = useState(false);
+  const { isOpen, onOpen, onClose } = useDisclosure();
+
+  const getStages = (url: string) => {
+    fetch(url, {
+      method: "GET",
+      headers: {
+        Accept: "application/json",
+      },
+    }).then(async (res) => {
+      const jsonObj = await res.json();
+      setData(jsonObj["stages"]);
+    });
+  };
+
+  useEffect(() => {
+    if (isOpen && !loaded) {
+      getStages("/api/job/" + props.value + "/stages");
+      setLoaded(true);
+    }
+  }, [stages, isOpen]);
+
+  return (
+    <Flex>
+      <Link onClick={onOpen} icon>
+        {props.value} <ExternalLinkIcon mx="2px" />
+      </Link>
+      <Modal isOpen={isOpen} size="small" onClose={onClose}>
+        <ModalOverlay />
+        <ModalContent>
+          <ModalHeader textAlign={"center"}>
+            Stages metrics for {props.value} job
+          </ModalHeader>
+          <ModalCloseButton />
+          <ModalBody margin="auto">
+            <JobStagesQueries stages={stages} />
+          </ModalBody>
+          <ModalFooter>
+            <Button colorScheme="blue" mr={3} onClick={onClose}>
+              Close
+            </Button>
+          </ModalFooter>
+        </ModalContent>
+      </Modal>
+    </Flex>
+  );
+};
+
 export const ProgressCell: (props: any) => React.ReactNode = (props: any) => {
   return (
     <CircularProgress value={props.value} color="orange.400">
@@ -141,7 +193,7 @@ const columns: Column<any>[] = [
   {
     Header: "Job ID",
     accessor: "job_id",
-    Cell: LinkCell,
+    Cell: JobLinkCell,
   },
   {
     Header: "Job Name",

Reply via email to