Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ SELECT
job_name,
run_attempt,
repo
FROM
misc.oss_ci_utilization_metadata
FROM misc.oss_ci_utilization_metadata
WHERE
workflow_id = { workflowId: UInt64}
AND repo = {repo: String }
workflow_id IN {workflowIds: Array(UInt64)}
AND repo IN {repos: Array(String)}
4 changes: 4 additions & 0 deletions torchci/components/additionalTestInfo/TestInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export function isPending(jobs: JobData[]) {
return jobs.some((job) => IsJobInProgress(job.conclusion));
}

export function isPendingJob(job: JobData) {
return IsJobInProgress(job.conclusion);
}

export function RecursiveDetailsSummary({
info,
level,
Expand Down
28 changes: 6 additions & 22 deletions torchci/components/commit/WorkflowBox.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button, Stack, styled, Tooltip, Typography } from "@mui/material";
import { isPending, TestInfo } from "components/additionalTestInfo/TestInfo";
import { isPending, isPendingJob, TestInfo } from "components/additionalTestInfo/TestInfo";
import styles from "components/commit/commit.module.css";
import LogViewer, { SearchLogViewer } from "components/common/log/LogViewer";
import { durationDisplay } from "components/common/TimeUtils";
Expand Down Expand Up @@ -44,14 +44,12 @@ const JobButton = styled(Button)({
});
function WorkflowJobSummary({
job,
utilMetadata,
artifacts,
artifactsToShow,
setArtifactsToShow,
unstableIssues,
}: {
job: JobData;
utilMetadata?: UtilizationMetadataInfo[];
artifacts?: Artifact[];
artifactsToShow: Set<string>;
setArtifactsToShow: any;
Expand Down Expand Up @@ -97,18 +95,13 @@ function WorkflowJobSummary({
</JobButton>
);
}
if (utilMetadata && utilMetadata.length > 0) {
if (utilMetadata.length > 1) {
console.log(
`Multiple util metadata found for job ${job.id}, currently only showing the first one`
);
}
const m = utilMetadata[0];
if (job.id && !isPendingJob(job)) {
const m = job;
subInfo.push(
<>
<JobButton
variant="outlined"
href={`/utilization/${m.workflow_id}/${m.job_id}/${m.run_attempt}`}
href={`/utilization/${m.workflowId}/${m.id}/${m.runAttempt}`}
data-ga-action="utilization_report_click"
data-ga-label="nav_button"
data-ga-category="user_interaction"
Expand Down Expand Up @@ -184,10 +177,6 @@ export default function WorkflowBox({
: styles.workflowBoxSuccess;

const anchorName = encodeURIComponent(workflowName.toLowerCase());

const { utilMetadataList } = useUtilMetadata(workflowId?.toString());
const groupUtilMetadataList = groupMetadataByJobId(utilMetadataList);

const { artifacts, error } = useArtifacts(jobs.map((job) => job.workflowId));
const [artifactsToShow, setArtifactsToShow] = useState(new Set<string>());
const groupedArtifacts = groupArtifacts(jobs, artifacts);
Expand Down Expand Up @@ -305,11 +294,6 @@ export default function WorkflowBox({
<div key={job.id} id={`${job.id}-box`}>
<WorkflowJobSummary
job={job}
utilMetadata={
job.id
? groupUtilMetadataList.get(job.id.toString())
: undefined
}
artifacts={groupedArtifacts?.get(job.id?.toString())}
artifactsToShow={artifactsToShow}
setArtifactsToShow={setArtifactsToShow}
Expand All @@ -330,13 +314,13 @@ export default function WorkflowBox({
);
}

function useUtilMetadata(workflowId: string | undefined): {
function useWorkflowsUtilMetadata(workflowIds: string[] | undefined): {
utilMetadataList: UtilizationMetadataInfo[];
metaError: any;
} {
const { data, error } =
useSWRImmutable<ListUtilizationMetadataInfoAPIResponse>(
`/api/list_utilization_metadata_info/${workflowId}`,
`/api/list_utilization_metadata_info/workflows? parameters={repo:"pytorch/pytorch", workflow_ids:${workflowIds}}`,
fetcher
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ function useUtilMetadata(workflowId: string | undefined): {
metaError: any;
} {
const { data, error } = useSWR<ListUtilizationMetadataInfoAPIResponse>(
`/api/list_utilization_metadata_info/${workflowId}?includes_stats=true`,
`/api/list_utilization_metadata_info/workflow/${workflowId}?includes_stats=true`,
fetcher,
{
refreshInterval: 20 * 60 * 1000, // refresh every 20 minuts
Expand Down
18 changes: 10 additions & 8 deletions torchci/lib/utilization/fetchListUtilizationMetadataInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const LIST_UTIL_METADATA_INFO_QUERY_FOLDER_NAME =
const LIST_UTIL_METADATA_WITH_STATS_QUERY =
"oss_ci_util/oss_ci_list_util_stats";

export default async function fetchListUtilizationMetadataInfo(
export async function fetchListUtilizationMetadataInfoForSingleWorkflow(
params: ListUtilizationMetadataInfoParams
): Promise<ListUtilizationMetadataInfoAPIResponse> {
let meta_resp = null;
Expand All @@ -29,8 +29,8 @@ export default async function fetchListUtilizationMetadataInfo(
`[api][list_utilization_metadata_info][${params.workflow_id}]list util metadata without runtime aggregated stats`
);
meta_resp = await listUtilizationMetadataInfo(
params.workflow_id,
params.repo
[params.workflow_id],
params.repo? [params.repo] : [UTILIZATION_DEFAULT_REPO]
);
}

Expand All @@ -45,15 +45,15 @@ export default async function fetchListUtilizationMetadataInfo(
};
}

async function listUtilizationMetadataInfo(
workflow_id: string,
repo: string = UTILIZATION_DEFAULT_REPO
export async function listUtilizationMetadataInfo(
workflow_ids: string[],
repos: string[] = [UTILIZATION_DEFAULT_REPO]
) {
const response = await queryClickhouseSaved(
LIST_UTIL_METADATA_INFO_QUERY_FOLDER_NAME,
{
workflowId: workflow_id,
repo: repo,
workflowIds: workflow_ids,
repo: repos,
}
);
return response;
Expand All @@ -79,6 +79,7 @@ async function listUtilizationMetadataWithStats(
}
return res;
}

function toMetadata(metadata: any) {
const data: UtilizationMetadataInfo = {
workflow_id: metadata.workflow_id,
Expand All @@ -90,6 +91,7 @@ function toMetadata(metadata: any) {
};
return data;
}

function toUtilizationStats(metadata: any) {
const stats: UtilizationAggreStats = {
cpu_max: metadata.cpu_max,
Expand Down
12 changes: 12 additions & 0 deletions torchci/lib/utilization/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ export const EMPTY_LIST_UTILIZATION_METADATA_INFO_API_RESPONSE: ListUtilizationM
metadata_list: [],
};


export const EMPTY_LIST_WORKFLOWS_UTILIZATION_METADATA_INFO_API_RESPONSE: ListWorkflowsUtilizationMetadataInfoAPIResponse =
{
metadata_map: {},
};
export interface UtilizationParams {
workflow_id: string;
job_id: string;
Expand Down Expand Up @@ -94,6 +99,13 @@ export interface UtilizationAggreStats {
gpu_allocated_memory_p90?: number;
}


export interface ListWorkflowsUtilizationMetadataInfoAPIResponse {
metadata_map:{
[key: number]: UtilizationMetadataInfo[];
}
}

/**
* The response of the API call to list utilization metadata info.
* @param metadata_list The list of utilization metadata info.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getErrorMessage } from "lib/error_utils";
import fetchListUtilizationMetadataInfo from "lib/utilization/fetchListUtilizationMetadataInfo";
import { fetchListUtilizationMetadataInfoForSingleWorkflow } from "lib/utilization/fetchListUtilizationMetadataInfo";
import {
EMPTY_LIST_UTILIZATION_METADATA_INFO_API_RESPONSE,
ListUtilizationMetadataInfoParams,
Expand All @@ -26,7 +26,7 @@ export default async function handler(
};

try {
const resp = await fetchListUtilizationMetadataInfo(params);
const resp = await fetchListUtilizationMetadataInfoForSingleWorkflow(params);
if (!resp) {
return res
.status(200)
Expand Down
85 changes: 85 additions & 0 deletions torchci/pages/api/list_utilization_metadata_info/workflows.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { readApiGetParams } from "lib/benchmark/api_helper/backend/common/utils";
import { getErrorMessage } from "lib/error_utils";
import { listUtilizationMetadataInfo } from "lib/utilization/fetchListUtilizationMetadataInfo";
import { EMPTY_LIST_WORKFLOWS_UTILIZATION_METADATA_INFO_API_RESPONSE } from "lib/utilization/types";
import { NextApiRequest, NextApiResponse } from "next";

/**
* API Route: /api/list_utilization_metadata/workflows
* Fetch benchmark time series data (e.g., compiler performance).
* currently only support compiler_precompute
*
* Supported Methods:
* - GET : Pass parameters via query string
* Example:
* /api/list_utilization_metadata/workflows?parameters={repo:"pytorch/pytorch", workflow_ids:["f1234567890"]}
* - POST : Pass parameters in JSON body
* Example:
* {
*
* repo: string,
* workflow_ids: Array<string>,
* }
**/
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "GET" && req.method !== "POST") {
res.setHeader("Allow", "GET, POST");
return res.status(405).json({ error: "Only GET and POST allowed" });
}
const params = readApiGetParams(req);
console.log("[API]list_utilization_metadata_info/workflows, received request:", params);

// validate params
if (
!params ||
!params.query_params ||
Object.keys(params).length == 0) {
return res.status(400).json({ error: "Missing parameters" });
}

if (!params.workflow_ids || params.workflow_ids.length == 0 || !params.repo) {
return res.status(400).json({ error: "Missing required parameters" });
}

try {
const resp = await fetchListUtilizationMetadataInfoForWorkflows(params);
if (!resp) {
return res
.status(200)
.json(EMPTY_LIST_WORKFLOWS_UTILIZATION_METADATA_INFO_API_RESPONSE);
}
return res.status(200).json(resp);
} catch (error) {
const err_msg = getErrorMessage(error);
console.error("[API]list_utilization_metadata_info/workflows, error: ", err_msg)
return res.status(500).json({ error: err_msg });
}
}



export async function fetchListUtilizationMetadataInfoForWorkflows(
params: any
): Promise<any> {
let workflowIds = []
if (params.workflow_id){
workflowIds = [params.workflow_id]
} else if (params.workflow_ids){
workflowIds = params.workflow_ids
}
let repos = [params.repo]
const meta_resp = await listUtilizationMetadataInfo(
workflowIds,
repos
);

if (!meta_resp || meta_resp.length == 0) {
return EMPTY_LIST_WORKFLOWS_UTILIZATION_METADATA_INFO_API_RESPONSE;
}
return {
metadata_map:
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ const JobUtilization = () => {
const router = useRouter();
const { workflowId, jobId, attempt } = router.query;

let shouldFetch = workflowId && jobId;

let { data, error } = useSWRImmutable(
`/api/utilization/${workflowId}/${jobId}/${attempt}`,
shouldFetch ? `/api/utilization/${workflowId}/${jobId}/${attempt}` : null,
fetcherHandleError,
{
errorRetryCount: 3,
Expand Down
Loading