Skip to content

Commit 167da65

Browse files
craig[bot]adityamarurafiss
committed
107210: jobs: enable downloading execution detail files r=maryliag a=adityamaru In cockroachdb#106879 we added a table to the `Advanced Debugging` tab of the job details page. This table lists out all the execution detail files that are available for the given job. This change is a follow up to add download functionality to each row in the table. The format of the downloaded file is determined by the prefix of the filename. A final change to allow users to generate execution details will be added in the next follow up. Informs: cockroachdb#105076 Release note: None 107760: spanconfigccl: fix tests under multitenancy r=yuzefovich a=rafiss fixes cockroachdb#106818 fixes cockroachdb#106821 Release note: None Co-authored-by: adityamaru <[email protected]> Co-authored-by: Rafi Shamim <[email protected]>
3 parents 2d47570 + 566f3cb + ee028ba commit 167da65

File tree

14 files changed

+191
-66
lines changed

14 files changed

+191
-66
lines changed

pkg/ccl/spanconfigccl/spanconfigsplitterccl/datadriven_test.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ func TestDataDriven(t *testing.T) {
6767
datadriven.Walk(t, datapathutils.TestDataPath(t), func(t *testing.T, path string) {
6868
tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{
6969
ServerArgs: base.TestServerArgs{
70-
// Fails with nil pointer dereference. Tracked with #76378 and #106818.
71-
DefaultTestTenant: base.TestDoesNotWorkWithSecondaryTenantsButWeDontKnowWhyYet(106818),
70+
DefaultTestTenant: base.TestControlsTenantsExplicitly,
7271
Knobs: base.TestingKnobs{
7372
SpanConfig: scKnobs,
7473
},
@@ -79,15 +78,10 @@ func TestDataDriven(t *testing.T) {
7978
spanConfigTestCluster := spanconfigtestcluster.NewHandle(t, tc, scKnobs)
8079
defer spanConfigTestCluster.Cleanup()
8180

82-
var tenant *spanconfigtestcluster.Tenant
83-
if strings.Contains(path, "tenant") {
84-
tenantID := roachpb.MustMakeTenantID(10)
85-
tenant = spanConfigTestCluster.InitializeTenant(ctx, tenantID)
86-
spanConfigTestCluster.AllowSecondaryTenantToSetZoneConfigurations(t, tenantID)
87-
spanConfigTestCluster.EnsureTenantCanSetZoneConfigurationsOrFatal(t, tenant)
88-
} else {
89-
tenant = spanConfigTestCluster.InitializeTenant(ctx, roachpb.SystemTenantID)
90-
}
81+
tenantID := roachpb.MustMakeTenantID(10)
82+
tenant := spanConfigTestCluster.InitializeTenant(ctx, tenantID)
83+
spanConfigTestCluster.AllowSecondaryTenantToSetZoneConfigurations(t, tenantID)
84+
spanConfigTestCluster.EnsureTenantCanSetZoneConfigurationsOrFatal(t, tenant)
9185

9286
// TODO(irfansharif): Expose this through the test harness once we integrate
9387
// it into the schema changer.
@@ -119,7 +113,8 @@ func TestDataDriven(t *testing.T) {
119113
var dbName, tbName string
120114
d.ScanArgs(t, "database", &dbName)
121115
d.ScanArgs(t, "table", &tbName)
122-
objID = tenant.LookupTableByName(ctx, dbName, tbName).GetID()
116+
tbl := tenant.LookupTableByName(ctx, dbName, tbName)
117+
objID = tbl.GetID()
123118
default:
124119
d.Fatalf(t, "insufficient/improper args (%v) provided to split", d.CmdArgs)
125120
}

pkg/ccl/spanconfigccl/spanconfigsqlwatcherccl/sqlwatcher_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,6 @@ func TestSQLWatcherMultiple(t *testing.T) {
287287

288288
tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{
289289
ServerArgs: base.TestServerArgs{
290-
DefaultTestTenant: base.TestDoesNotWorkWithSecondaryTenantsButWeDontKnowWhyYet(106821),
291290
Knobs: base.TestingKnobs{
292291
SpanConfig: &spanconfig.TestingKnobs{
293292
ManagerDisableJobCreation: true, // disable the automatic job creation.
@@ -305,7 +304,7 @@ func TestSQLWatcherMultiple(t *testing.T) {
305304

306305
noopCheckpointDuration := 100 * time.Millisecond
307306
sqlWatcher := spanconfigsqlwatcher.New(
308-
ts.Codec(),
307+
ts.ApplicationLayer().Codec(),
309308
ts.ClusterSettings(),
310309
ts.RangeFeedFactory().(*rangefeed.Factory),
311310
1<<20, /* 1 MB, bufferMemLimit */

pkg/ui/workspaces/cluster-ui/src/api/jobProfilerApi.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ export type ListJobProfilerExecutionDetailsRequest =
1616
export type ListJobProfilerExecutionDetailsResponse =
1717
cockroach.server.serverpb.ListJobProfilerExecutionDetailsResponse;
1818

19-
export const getExecutionDetails = (
19+
export type GetJobProfilerExecutionDetailRequest =
20+
cockroach.server.serverpb.GetJobProfilerExecutionDetailRequest;
21+
export type GetJobProfilerExecutionDetailResponse =
22+
cockroach.server.serverpb.GetJobProfilerExecutionDetailResponse;
23+
24+
export const listExecutionDetailFiles = (
2025
req: ListJobProfilerExecutionDetailsRequest,
2126
): Promise<cockroach.server.serverpb.ListJobProfilerExecutionDetailsResponse> => {
2227
return fetchData(
@@ -27,3 +32,15 @@ export const getExecutionDetails = (
2732
"30M",
2833
);
2934
};
35+
36+
export const getExecutionDetailFile = (
37+
req: GetJobProfilerExecutionDetailRequest,
38+
): Promise<cockroach.server.serverpb.GetJobProfilerExecutionDetailResponse> => {
39+
return fetchData(
40+
cockroach.server.serverpb.GetJobProfilerExecutionDetailResponse,
41+
`/_status/job_profiler_execution_details/${req.job_id}/${req.filename}`,
42+
null,
43+
null,
44+
"30M",
45+
);
46+
};

pkg/ui/workspaces/cluster-ui/src/downloadFile/downloadFile.tsx

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,13 @@ import React, {
1515
useImperativeHandle,
1616
} from "react";
1717

18-
type FileTypes = "text/plain" | "application/json";
19-
2018
export interface DownloadAsFileProps {
2119
fileName?: string;
22-
fileType?: FileTypes;
23-
content?: string;
20+
content?: Blob;
2421
}
2522

2623
export interface DownloadFileRef {
27-
download: (name: string, type: FileTypes, body: string) => void;
24+
download: (name: string, body: Blob) => void;
2825
}
2926

3027
/*
@@ -58,26 +55,25 @@ export interface DownloadFileRef {
5855
// tslint:disable-next-line:variable-name
5956
export const DownloadFile = forwardRef<DownloadFileRef, DownloadAsFileProps>(
6057
(props, ref) => {
61-
const { children, fileName, fileType, content } = props;
58+
const { children, fileName, content } = props;
6259
const anchorRef = useRef<HTMLAnchorElement>();
6360

64-
const bootstrapFile = (name: string, type: FileTypes, body: string) => {
61+
const bootstrapFile = (name: string, body: Blob) => {
6562
const anchorElement = anchorRef.current;
66-
const file = new Blob([body], { type });
67-
anchorElement.href = URL.createObjectURL(file);
63+
anchorElement.href = URL.createObjectURL(body);
6864
anchorElement.download = name;
6965
};
7066

7167
useEffect(() => {
7268
if (content === undefined) {
7369
return;
7470
}
75-
bootstrapFile(fileName, fileType, content);
76-
}, [fileName, fileType, content]);
71+
bootstrapFile(fileName, content);
72+
}, [fileName, content]);
7773

7874
useImperativeHandle(ref, () => ({
79-
download: (name: string, type: FileTypes, body: string) => {
80-
bootstrapFile(name, type, body);
75+
download: (name: string, body: Blob) => {
76+
bootstrapFile(name, body);
8177
anchorRef.current.click();
8278
},
8379
}));

pkg/ui/workspaces/cluster-ui/src/jobs/jobDetailsPage/jobDetails.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import jobStyles from "src/jobs/jobs.module.scss";
4040
import classNames from "classnames/bind";
4141
import { Timestamp } from "../../timestamp";
4242
import {
43+
GetJobProfilerExecutionDetailRequest,
44+
GetJobProfilerExecutionDetailResponse,
4345
ListJobProfilerExecutionDetailsRequest,
4446
ListJobProfilerExecutionDetailsResponse,
4547
RequestState,
@@ -60,14 +62,17 @@ enum TabKeysEnum {
6062

6163
export interface JobDetailsStateProps {
6264
jobRequest: RequestState<JobResponse>;
63-
jobProfilerResponse: RequestState<ListJobProfilerExecutionDetailsResponse>;
65+
jobProfilerExecutionDetailFilesResponse: RequestState<ListJobProfilerExecutionDetailsResponse>;
6466
jobProfilerLastUpdated: moment.Moment;
6567
jobProfilerDataIsValid: boolean;
68+
onDownloadExecutionFileClicked: (
69+
req: GetJobProfilerExecutionDetailRequest,
70+
) => Promise<GetJobProfilerExecutionDetailResponse>;
6671
}
6772

6873
export interface JobDetailsDispatchProps {
6974
refreshJob: (req: JobRequest) => void;
70-
refreshExecutionDetails: (
75+
refreshExecutionDetailFiles: (
7176
req: ListJobProfilerExecutionDetailsRequest,
7277
) => void;
7378
}
@@ -130,10 +135,15 @@ export class JobDetails extends React.Component<
130135
return (
131136
<JobProfilerView
132137
jobID={id}
133-
executionDetailsResponse={this.props.jobProfilerResponse}
134-
refreshExecutionDetails={this.props.refreshExecutionDetails}
138+
executionDetailFilesResponse={
139+
this.props.jobProfilerExecutionDetailFilesResponse
140+
}
141+
refreshExecutionDetailFiles={this.props.refreshExecutionDetailFiles}
135142
lastUpdated={this.props.jobProfilerLastUpdated}
136143
isDataValid={this.props.jobProfilerDataIsValid}
144+
onDownloadExecutionFileClicked={
145+
this.props.onDownloadExecutionFileClicked
146+
}
137147
/>
138148
);
139149
};

pkg/ui/workspaces/cluster-ui/src/jobs/jobDetailsPage/jobDetailsConnected.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { selectID } from "../../selectors";
2323
import {
2424
ListJobProfilerExecutionDetailsRequest,
2525
createInitialState,
26+
getExecutionDetailFile,
2627
} from "src/api";
2728
import {
2829
initialState,
@@ -39,15 +40,17 @@ const mapStateToProps = (
3940
const jobID = selectID(state, props);
4041
return {
4142
jobRequest: state.adminUI?.job?.cachedData[jobID] ?? emptyState,
42-
jobProfilerResponse: state.adminUI?.executionDetails ?? initialState,
43-
jobProfilerLastUpdated: state.adminUI?.executionDetails?.lastUpdated,
44-
jobProfilerDataIsValid: state.adminUI?.executionDetails?.valid,
43+
jobProfilerExecutionDetailFilesResponse:
44+
state.adminUI?.executionDetailFiles ?? initialState,
45+
jobProfilerLastUpdated: state.adminUI?.executionDetailFiles?.lastUpdated,
46+
jobProfilerDataIsValid: state.adminUI?.executionDetailFiles?.valid,
47+
onDownloadExecutionFileClicked: getExecutionDetailFile,
4548
};
4649
};
4750

4851
const mapDispatchToProps = (dispatch: Dispatch): JobDetailsDispatchProps => ({
4952
refreshJob: (req: JobRequest) => jobActions.refresh(req),
50-
refreshExecutionDetails: (req: ListJobProfilerExecutionDetailsRequest) =>
53+
refreshExecutionDetailFiles: (req: ListJobProfilerExecutionDetailsRequest) =>
5154
dispatch(jobProfilerActions.refresh(req)),
5255
});
5356

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
@import "src/core/index.module";
22

3+
.crl-job-profiler-view {
4+
&__actions-column {
5+
display: flex;
6+
flex-direction: row;
7+
flex-wrap: nowrap;
8+
justify-content: flex-end;
9+
}
10+
}
11+
12+
.column-size-medium {
13+
width: 230px;
14+
}
15+
16+
.download-execution-detail-button {
17+
white-space: nowrap;
18+
19+
>svg {
20+
margin-right: $spacing-x-small;
21+
}
22+
}
23+
324
.sorted-table {
425
width: 100%;
526
}

0 commit comments

Comments
 (0)