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 May 26, 2023
1 parent 23de673 commit ca793e2
Showing 1 changed file with 56 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import "antd/lib/row/style";
import "antd/lib/tabs/style";
import Long from "long";
import Helmet from "react-helmet";
import { RouteComponentProps } from "react-router-dom";
import { RouteComponentProps, useHistory, useLocation } from "react-router-dom";
import { JobRequest, JobResponse } from "src/api/jobsApi";
import { Button } from "src/button";
import { Loading } from "src/loading";
Expand Down Expand Up @@ -47,27 +47,40 @@ const jobCx = classNames.bind(jobStyles);

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

export interface JobDetailsStateProps {
job: JobResponse;
jobError: Error | null;
jobLoading: boolean;
currentTab?: string;
}

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 +112,28 @@ 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 (profiles all nodes; MEMORY OVERHEAD)"
value={<a href={url}>Profile</a>}
/>
</SummaryCard>
</Col>
</Row>
);
};

renderOverviewTabContent = (
hasNextRun: boolean,
nextRun: moment.Moment,
Expand Down Expand Up @@ -164,12 +199,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.job || this.props.jobLoading;
const error = this.props.jobError;
const job = this.props.job;
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 @@ -209,10 +258,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>
<TabPane tab={TabKeysEnum.PROFILER} key="profiler">
{this.renderProfilerTabContent(job)}
</TabPane>
</Tabs>
</>
)}
Expand Down

0 comments on commit ca793e2

Please sign in to comment.