Skip to content

Commit

Permalink
jobs: add a Profiler tab to the job details page
Browse files Browse the repository at this point in the history
This change adds a Profiler tab to the job details
page. This change also adds a row that allows collection
of a cluster-wide CPU profile for 5 seconds, of all the
samples corresponding to the job's execution.

Fixes: #102735

Release note (ui change): job details page now has a
profiler tab for more advanced observability into a job's
execution. Currently, we support collecting a cluster-wide
CPU profile of the job.
  • Loading branch information
adityamaru committed Jun 14, 2023
1 parent f88077d commit 34332f0
Showing 1 changed file with 59 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
import React from "react";
import React, { useContext } from "react";
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { ArrowLeft } from "@cockroachlabs/icons";
import { Col, Row, Tabs } from "antd";
Expand Down Expand Up @@ -41,6 +41,8 @@ import classNames from "classnames/bind";
import { Timestamp } from "../../timestamp";
import { RequestState } from "../../api";
import moment from "moment-timezone";
import { CockroachCloudContext } from "src/contexts";
import { InlineAlert } from "@cockroachlabs/ui-components";

const { TabPane } = Tabs;

Expand All @@ -49,6 +51,7 @@ const jobCx = classNames.bind(jobStyles);

enum TabKeysEnum {
OVERVIEW = "Overview",
PROFILER = "Profiler",
}

export interface JobDetailsStateProps {
Expand All @@ -59,15 +62,26 @@ export interface JobDetailsDispatchProps {
refreshJob: (req: JobRequest) => void;
}

export interface JobDetailsState {
currentTab?: string;
}

export type JobDetailsProps = JobDetailsStateProps &
JobDetailsDispatchProps &
RouteComponentProps<unknown>;

export class JobDetails extends React.Component<JobDetailsProps> {
export class JobDetails extends React.Component<
JobDetailsProps,
JobDetailsState
> {
refreshDataInterval: NodeJS.Timeout;

constructor(props: JobDetailsProps) {
super(props);
const searchParams = new URLSearchParams(props.history.location.search);
this.state = {
currentTab: searchParams.get("tab") || "overview",
};
}

private refresh(): void {
Expand Down Expand Up @@ -99,6 +113,30 @@ export class JobDetails extends React.Component<JobDetailsProps> {

prevPage = (): void => this.props.history.goBack();

renderProfilerTabContent = (
job: cockroach.server.serverpb.JobResponse,
): React.ReactElement => {
const id = job?.id;
// This URL results in a cluster-wide CPU profile to be collected for 5
// seconds. We set `tagfocus` (tf) to only view the samples corresponding to
// this job's execution.
const url = `debug/pprof/ui/cpu?node=all&seconds=5&labels=true&tf=job.*${id}`;
return (
<Row gutter={24}>
<Col className="gutter-row" span={24}>
<SummaryCard className={cardCx("summary-card")}>
<SummaryCardItem
label="Cluster-wide CPU Profile"
value={<a href={url}>Profile</a>}
/>
<InlineAlert intent="warning" title="This profiles all nodes in the cluster.
This operation buffers profiles in memory and should only be run if there is sufficient overhead." />
</SummaryCard>
</Col>
</Row>
);
};

renderOverviewTabContent = (
hasNextRun: boolean,
nextRun: moment.Moment,
Expand Down Expand Up @@ -193,12 +231,26 @@ export class JobDetails extends React.Component<JobDetailsProps> {
);
};

onTabChange = (tabId: string): void => {
const { history } = this.props;
const searchParams = new URLSearchParams(history.location.search);
searchParams.set("tab", tabId);
history.replace({
...history.location,
search: searchParams.toString(),
});
this.setState({
currentTab: tabId,
});
};

render(): React.ReactElement {
const isLoading = this.props.jobRequest.inFlight;
const error = this.props.jobRequest.error;
const job = this.props.jobRequest.data;
const nextRun = TimestampToMoment(job?.next_run);
const hasNextRun = nextRun?.isAfter();
const { currentTab } = this.state;
return (
<div className={jobCx("job-details")}>
<Helmet title={"Details | Job"} />
Expand Down Expand Up @@ -238,10 +290,15 @@ export class JobDetails extends React.Component<JobDetailsProps> {
<Tabs
className={commonStyles("cockroach--tabs")}
defaultActiveKey={TabKeysEnum.OVERVIEW}
onChange={this.onTabChange}
activeKey={currentTab}
>
<TabPane tab={TabKeysEnum.OVERVIEW} key="overview">
{this.renderOverviewTabContent(hasNextRun, nextRun, job)}
</TabPane>
{!useContext(CockroachCloudContext) && (<TabPane tab={TabKeysEnum.PROFILER} key="profiler">
{this.renderProfilerTabContent(job)}
</TabPane>)}
</Tabs>
</>
)}
Expand Down

0 comments on commit 34332f0

Please sign in to comment.