From ecc4607f7d72c32ccbe9e5309900e0693bdefc1b Mon Sep 17 00:00:00 2001 From: popcorny Date: Tue, 29 Oct 2024 17:16:59 +0800 Subject: [PATCH] The checkdetail should reload the lastRun if a run is still running Signed-off-by: popcorny --- js/src/components/check/CheckDetail.tsx | 109 ++++++++++-------------- js/src/components/run/RunResultPane.tsx | 19 +---- js/src/components/run/RunView.tsx | 22 ++--- js/src/lib/hooks/useRun.tsx | 28 +++--- recce/apis/check_api.py | 2 - 5 files changed, 67 insertions(+), 113 deletions(-) diff --git a/js/src/components/check/CheckDetail.tsx b/js/src/components/check/CheckDetail.tsx index 06128634..7d834f50 100644 --- a/js/src/components/check/CheckDetail.tsx +++ b/js/src/components/check/CheckDetail.tsx @@ -1,9 +1,4 @@ import { - Accordion, - AccordionButton, - AccordionIcon, - AccordionItem, - AccordionPanel, Box, Button, Center, @@ -55,16 +50,17 @@ import { stripIndents } from "common-tags"; import { useClipBoardToast } from "@/lib/hooks/useClipBoardToast"; import { buildTitle, buildDescription, buildQuery } from "./check"; import SqlEditor, { DualSqlEditor } from "../query/SqlEditor"; -import { useCallback, useEffect, useState } from "react"; -import { cancelRun, submitRunFromCheck, waitRun } from "@/lib/api/runs"; +import { useCallback, useState } from "react"; +import { cancelRun, submitRunFromCheck } from "@/lib/api/runs"; import { Run } from "@/lib/api/types"; import { RunView } from "../run/RunView"; -import { formatDistanceToNow } from "date-fns"; +import { formatDistanceToNow, sub } from "date-fns"; import { LineageDiffView } from "./LineageDiffView"; import { findByRunType } from "../run/registry"; import { PresetCheckTemplateView } from "./PresetCheckTemplateView"; import { VSplit } from "../split/Split"; import { useCopyToClipboardButton } from "@/lib/hooks/ScreenShot"; +import { useRun } from "@/lib/hooks/useRun"; interface CheckDetailProps { checkId: string; @@ -74,9 +70,9 @@ export const CheckDetail = ({ checkId }: CheckDetailProps) => { const queryClient = useQueryClient(); const [, setLocation] = useLocation(); const { successToast, failToast } = useClipBoardToast(); - const [runId, setRunId] = useState(); + const [submittedRunId, setSubmittedRunId] = useState(); const [progress, setProgress] = useState(); - const [abort, setAborting] = useState(false); + const [isAborting, setAborting] = useState(false); const { isOpen: isPresetCheckTemplateOpen, onOpen: onPresetCheckTemplateOpen, @@ -90,15 +86,19 @@ export const CheckDetail = ({ checkId }: CheckDetailProps) => { const { isLoading, error, - refetch, data: check, } = useQuery({ queryKey: cacheKeys.check(checkId), queryFn: async () => getCheck(checkId), - refetchOnMount: false, - staleTime: 5 * 60 * 1000, + refetchOnMount: true, }); + const trackedRunId = submittedRunId || check?.last_run?.run_id; + const { run, error: rerunError } = useRun(trackedRunId); + const isRunning = submittedRunId + ? !run || run.status === "running" + : run?.status === "running"; + const runTypeEntry = check?.type ? findByRunType(check?.type) : undefined; const isPresetCheck = check?.is_preset || false; @@ -118,52 +118,24 @@ export const CheckDetail = ({ checkId }: CheckDetailProps) => { }, }); - const submitRunFn = async () => { + const handleRerun = useCallback(async () => { const type = check?.type; if (!type) { return; } - const { run_id } = await submitRunFromCheck(checkId, { nowait: true }); - - setRunId(run_id); - - while (true) { - const run = await waitRun(run_id, 2); - setProgress(run.progress); - if (run.result || run.error) { - setAborting(false); - setProgress(undefined); - return run; - } - } - }; - - const { - data: rerunRun, - mutate: rerun, - error: rerunError, - isIdle: rerunIdle, - isPending: rerunPending, - } = useMutation({ - mutationFn: submitRunFn, - onSuccess: (run) => { - refetch(); - }, - }); - - const handleRerun = async () => { - rerun(); - }; + const submittedRun = await submitRunFromCheck(checkId, { nowait: true }); + setSubmittedRunId(submittedRun.run_id); + }, [check, checkId, setSubmittedRunId]); const handleCancel = useCallback(async () => { setAborting(true); - if (!runId) { + if (!trackedRunId) { return; } - return await cancelRun(runId); - }, [runId]); + return await cancelRun(trackedRunId); + }, [trackedRunId]); const handleCopy = async () => { if (!check) { @@ -213,7 +185,6 @@ export const CheckDetail = ({ checkId }: CheckDetailProps) => { return
Error: {error.message}
; } - const run = rerunIdle ? check?.last_run : rerunRun; const relativeTime = run?.run_at ? formatDistanceToNow(new Date(run.run_at), { addSuffix: true }) : null; @@ -284,7 +255,7 @@ export const CheckDetail = ({ checkId }: CheckDetailProps) => { } @@ -352,22 +323,28 @@ export const CheckDetail = ({ checkId }: CheckDetailProps) => { - {runTypeEntry?.RunResultView && ( - - )} + {runTypeEntry?.RunResultView && + (check?.last_run || trackedRunId ? ( + + ) : ( +
+ +
+ ))} {check && check.type === "schema_diff" && ( )} diff --git a/js/src/components/run/RunResultPane.tsx b/js/src/components/run/RunResultPane.tsx index 11cbd082..ed2906a7 100644 --- a/js/src/components/run/RunResultPane.tsx +++ b/js/src/components/run/RunResultPane.tsx @@ -59,21 +59,7 @@ export const _LoadableRunView = ({ onClose?: () => void; }) => { const { runAction } = useRecceActionContext(); - - const { isPending, error, run, onCancel } = useRun(runId); - const isPendingRef = useRef(false); - isPendingRef.current = isPending; - - useEffect(() => { - return () => { - if (isPendingRef.current) { - onCancel(); - } - }; - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isPendingRef]); - + const { error, run, onCancel, isRunning } = useRun(runId); const [viewOptions, setViewOptions] = useState(); const queryClient = useQueryClient(); const [, setLocation] = useLocation(); @@ -133,7 +119,7 @@ export const _LoadableRunView = ({ - - ); - } + } + + if (!run) { return (
- No Data + Loading...
); } diff --git a/js/src/lib/hooks/useRun.tsx b/js/src/lib/hooks/useRun.tsx index 9d53be28..8c0f8da1 100644 --- a/js/src/lib/hooks/useRun.tsx +++ b/js/src/lib/hooks/useRun.tsx @@ -9,39 +9,41 @@ import { useRunsAggregated } from "./LineageGraphContext"; interface UseRunResult { run?: Run; aborting: boolean; - isPending: boolean; + isRunning: boolean; error: Error | null; onCancel: () => void; RunResultView?: React.ComponentType; } export const useRun = (runId?: string): UseRunResult => { - const [isPolling, setIsPolling] = useState(false); + const [isRunning, setIsRunning] = useState(false); const [aborting, setAborting] = useState(false); const [, refetchRunsAggregated] = useRunsAggregated(); const { error, data: run } = useQuery({ queryKey: cacheKeys.run(runId || ""), queryFn: async () => { - return waitRun(runId || "", 2); + return waitRun(runId || "", isRunning ? 2 : 0); }, enabled: !!runId, - refetchInterval: isPolling ? 50 : false, + refetchInterval: isRunning ? 50 : false, retry: false, }); useEffect(() => { if (error || run?.result || run?.error) { - if (isPolling) { - setIsPolling(false); - if (run?.type === "row_count_diff") { - refetchRunsAggregated(); - } + if (isRunning) { + setIsRunning(false); } - } else { - setIsPolling(true); + if (run?.type === "row_count_diff") { + refetchRunsAggregated(); + } + } + + if (run?.status === "running") { + setIsRunning(true); } - }, [run, error, isPolling, refetchRunsAggregated]); + }, [run, error, isRunning, refetchRunsAggregated]); const onCancel = useCallback(async () => { setAborting(true); @@ -58,7 +60,7 @@ export const useRun = (runId?: string): UseRunResult => { return { run, - isPending: isPolling, + isRunning, aborting, error, onCancel, diff --git a/recce/apis/check_api.py b/recce/apis/check_api.py index 9cdff2b4..85ce247e 100644 --- a/recce/apis/check_api.py +++ b/recce/apis/check_api.py @@ -120,8 +120,6 @@ async def get_check_handler(check_id: UUID): raise HTTPException(status_code=404, detail='Not Found') runs = RunDAO().list_by_check_id(check_id) - # only get the last with successful result - runs = [run for run in runs if run.result is not None] last_run = runs[-1] if len(runs) > 0 else None out = CheckOut.from_check(check)