Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/components/shared/AnnouncementBanners.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import "@/config/announcements";

import { useState } from "react";

import { InfoBox } from "@/components/shared/InfoBox";
import { BlockStack } from "@/components/ui/layout";
import "@/config/announcements";
import { getStorage } from "@/utils/typedStorage";

interface DismissedAnnouncementsStorage {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Link } from "@/components/ui/link";
import { Spinner } from "@/components/ui/spinner";
import { useBackend } from "@/providers/BackendProvider";
import { getBackendStatusString } from "@/utils/backend";
import { CONTAINER_STATUSES_PRE_LAUNCH } from "@/utils/executionStatus";

const LogDisplay = ({
logs,
Expand Down Expand Up @@ -52,40 +53,29 @@ const LogDisplay = ({
);
};

/**
* Statuses where the container is running and actively producing logs.
* Used to decide whether to poll for new log content.
*/
const isStatusActivelyLogging = (status?: string): boolean => {
if (!status) {
return false;
}
switch (status) {
case "RUNNING":
case "PENDING":
case "QUEUED":
case "WAITING_FOR_UPSTREAM":
case "CANCELLING":
return true;
default:
return false;
}
};

/**
* Returns true if the container may have logs worth fetching.
*/
const shouldStatusHaveLogs = (status?: string): boolean => {
if (!status) {
return false;
}

if (isStatusActivelyLogging(status)) {
return true;
}

switch (status) {
case "FAILED":
case "SYSTEM_ERROR":
case "SUCCEEDED":
case "CANCELLED":
return true;
default:
return false;
}
return !CONTAINER_STATUSES_PRE_LAUNCH.has(status);
};

const getLogs = async (executionId: string, backendUrl: string) => {
Expand All @@ -104,27 +94,21 @@ const Logs = ({
}) => {
const { backendUrl, configured, available } = useBackend();

const [isLogging, setIsLogging] = useState(!!executionId);
const [shouldHaveLogs, setShouldHaveLogs] = useState(!!executionId);
const shouldFetch = !!executionId && shouldStatusHaveLogs(status);
const shouldPoll = shouldFetch && isStatusActivelyLogging(status);

const [logs, setLogs] = useState<{
log_text?: string;
system_error_exception_full?: string;
}>();
const { data, isLoading, error, refetch } = useQuery({
queryKey: ["logs", executionId],
queryFn: () => getLogs(String(executionId), backendUrl),
enabled: isLogging,
refetchInterval: 5000,
enabled: shouldFetch,
refetchInterval: shouldPoll ? 5000 : false,
refetchIntervalInBackground: false,
});

useEffect(() => {
if (status) {
setIsLogging(isStatusActivelyLogging(status));
setShouldHaveLogs(shouldStatusHaveLogs(status));
}
}, [status]);

useEffect(() => {
if (data && !error) {
setLogs({
Expand All @@ -139,8 +123,10 @@ const Logs = ({
}, [data, error]);

useEffect(() => {
refetch();
}, [backendUrl, refetch]);
if (shouldFetch) {
refetch();
}
}, [backendUrl, refetch, shouldFetch]);

if (!configured) {
return (
Expand All @@ -150,7 +136,7 @@ const Logs = ({
);
}

if (!shouldHaveLogs) {
if (!shouldFetch && !logs) {
return (
<InfoBox title="No logs available" variant="info">
Logs are available only for active, queued and completed executions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,20 @@ describe("OpenLogsInNewWindowLink", () => {
const statusesThatShouldHaveLogs: ContainerExecutionStatus[] = [
"RUNNING",
"PENDING",
"QUEUED",
"WAITING_FOR_UPSTREAM",
"CANCELLING",
"FAILED",
"SYSTEM_ERROR",
"SUCCEEDED",
// CANCELLED may have logs when the task was cancelled mid-run; the
// backend uploads logs before termination in that path.
"CANCELLED",
];

const statusesThatShouldNotHaveLogs: ContainerExecutionStatus[] = [
"INVALID",
"UNINITIALIZED",
"QUEUED",
"WAITING_FOR_UPSTREAM",
"SKIPPED",
];

Expand Down
1 change: 1 addition & 0 deletions src/components/shared/TaskDetails/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const TaskDetailsInternal = ({
<ExecutionDetails
executionId={executionId}
componentSpec={hydratedComponentRef.spec}
status={status}
className={BASE_BLOCK_CLASS}
/>
)}
Expand Down
8 changes: 7 additions & 1 deletion src/components/shared/TaskDetails/ExecutionDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import { InfoBox } from "../InfoBox";
interface ExecutionDetailsProps {
executionId: string;
componentSpec?: ComponentSpec;
status?: string;
className?: string;
}

export const ExecutionDetails = ({
executionId,
componentSpec,
status,
className,
}: ExecutionDetailsProps) => {
const { backendUrl } = useBackend();
Expand All @@ -35,7 +37,11 @@ export const ExecutionDetails = ({
data: containerState,
isLoading: isLoadingContainerState,
error: containerStateError,
} = useFetchContainerExecutionState(executionId, backendUrl);
} = useFetchContainerExecutionState(
isSubgraph ? undefined : executionId,
backendUrl,
status,
);

const getExecutionItems = () => {
const items: AttributeProps[] = [
Expand Down
7 changes: 6 additions & 1 deletion src/services/executionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
TWENTY_FOUR_HOURS_IN_MS,
} from "@/utils/constants";
import {
CONTAINER_STATUSES_PRE_LAUNCH,
flattenExecutionStatusStats,
getOverallExecutionStatusFromStats,
} from "@/utils/executionStatus";
Expand Down Expand Up @@ -69,11 +70,15 @@ const fetchContainerExecutionState = async (
export const useFetchContainerExecutionState = (
executionId: string | undefined,
backendUrl: string,
status?: string,
) => {
const shouldFetch =
!!executionId && (!status || !CONTAINER_STATUSES_PRE_LAUNCH.has(status));

return useQuery<GetContainerExecutionStateResponse>({
queryKey: ["container-execution-state", executionId],
queryFn: () => fetchContainerExecutionState(executionId!, backendUrl),
enabled: !!executionId,
enabled: shouldFetch,
refetchOnWindowFocus: false,
});
};
Expand Down
17 changes: 17 additions & 0 deletions src/utils/executionStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ export const EXECUTION_STATUS_BG_COLORS: Record<string, string> = {
UNINITIALIZED: "bg-yellow-400",
};

/**
* Statuses where the container was never launched.
* Used to skip fetches to /container_state and /container_log — both of which
* require a container execution record that won't exist for these statuses.
*
* CANCELLED is excluded: a task cancelled mid-run (while PENDING or RUNNING)
* will have a container execution record and uploaded logs, and the frontend
* cannot distinguish that from a pre-launch cancellation by status alone.
*/
export const CONTAINER_STATUSES_PRE_LAUNCH = new Set([
"INVALID",
"UNINITIALIZED",
"QUEUED",
"WAITING_FOR_UPSTREAM",
"SKIPPED",
]);

/**
* Statuses considered "in progress" (not terminal).
*/
Expand Down