Skip to content

Commit

Permalink
Merge #101607
Browse files Browse the repository at this point in the history
101607: jobs,sql: introduce SHOW JOB...WITH EXECUTION DETAILS r=miretskiy a=adityamaru

This change introduces a new option to the SHOW JOB and SHOW JOBS statements. By specifying `WITH EXECUTION DETAILS` users can render additional observability information about a job that is collected during the job's lifetime. Currently, the only piece of information is the URL to the latest DistSQL plan being run by the job. Going forward `EXECUTION DETAILS` is expected to be a relatively expensive operation that will scan select rows of the job_info table as determined by a specific job type.

The SHOW JOB query delegates to crdb_internal.job_execution_details that renders a JSONB of all the execution details collected over the lifetime of the job.

Release note: None
Informs: #101743
Epic: CRDB-8964

Co-authored-by: adityamaru <[email protected]>
  • Loading branch information
craig[bot] and adityamaru committed Apr 21, 2023
2 parents 979abd8 + 661796c commit 8124bff
Show file tree
Hide file tree
Showing 24 changed files with 433 additions and 37 deletions.
3 changes: 3 additions & 0 deletions docs/generated/sql/bnf/show_jobs.bnf
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
show_jobs_stmt ::=
'SHOW' 'AUTOMATIC' 'JOBS'
| 'SHOW' 'JOBS'
| 'SHOW' 'JOBS' 'WITH' show_job_options_list
| 'SHOW' 'CHANGEFEED' 'JOBS'
| 'SHOW' 'JOBS' select_stmt
| 'SHOW' 'JOBS' select_stmt 'WITH' show_job_options_list
| 'SHOW' 'JOBS' 'WHEN' 'COMPLETE' select_stmt
| 'SHOW' 'JOBS' for_schedules_clause
| 'SHOW' 'CHANGEFEED' 'JOBS' select_stmt
| 'SHOW' 'JOB' job_id
| 'SHOW' 'JOB' job_id 'WITH' show_job_options_list
| 'SHOW' 'CHANGEFEED' 'JOB' job_id
| 'SHOW' 'JOB' 'WHEN' 'COMPLETE' job_id
9 changes: 9 additions & 0 deletions docs/generated/sql/bnf/stmt_block.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -853,12 +853,15 @@ show_partitions_stmt ::=
show_jobs_stmt ::=
'SHOW' 'AUTOMATIC' 'JOBS'
| 'SHOW' 'JOBS'
| 'SHOW' 'JOBS' 'WITH' show_job_options_list
| 'SHOW' 'CHANGEFEED' 'JOBS'
| 'SHOW' 'JOBS' select_stmt
| 'SHOW' 'JOBS' select_stmt 'WITH' show_job_options_list
| 'SHOW' 'JOBS' 'WHEN' 'COMPLETE' select_stmt
| 'SHOW' 'JOBS' for_schedules_clause
| 'SHOW' 'CHANGEFEED' 'JOBS' select_stmt
| 'SHOW' 'JOB' a_expr
| 'SHOW' 'JOB' a_expr 'WITH' show_job_options_list
| 'SHOW' 'CHANGEFEED' 'JOB' a_expr
| 'SHOW' 'JOB' 'WHEN' 'COMPLETE' a_expr

Expand Down Expand Up @@ -1962,6 +1965,9 @@ for_grantee_clause ::=
'FOR' role_spec_list
|

show_job_options_list ::=
( show_job_options ) ( ( ',' show_job_options ) )*

opt_schedule_executor_type ::=
'FOR' 'BACKUP'
| 'FOR' 'SQL' 'STATISTICS'
Expand Down Expand Up @@ -2724,6 +2730,9 @@ targets_roles ::=
| 'TYPE' type_name_list
| grant_targets

show_job_options ::=
'EXECUTION' 'DETAILS'

show_ranges_options ::=
( 'TABLES' | 'INDEXES' | 'DETAILS' | 'KEYS' | 'EXPLAIN' ) ( ( ',' 'TABLES' | ',' 'INDEXES' | ',' 'DETAILS' | ',' 'EXPLAIN' | ',' 'KEYS' ) )*

Expand Down
2 changes: 2 additions & 0 deletions docs/generated/sql/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3181,6 +3181,8 @@ Note: the name pattern must contain ASCII letters already for capital letters to
<tr><td><a name="crdb_internal.is_constraint_active"></a><code>crdb_internal.is_constraint_active(table_name: <a href="string.html">string</a>, constraint_name: <a href="string.html">string</a>) &rarr; <a href="bool.html">bool</a></code></td><td><span class="funcdesc"><p>This function is used to determine if a given constraint is currently.
active for the current transaction.</p>
</span></td><td>Volatile</td></tr>
<tr><td><a name="crdb_internal.job_execution_details"></a><code>crdb_internal.job_execution_details(job_id: <a href="int.html">int</a>) &rarr; jsonb</code></td><td><span class="funcdesc"><p>Output a JSONB version of the specified job’s execution details. The execution details are collectedand persisted during the lifetime of the job and provide more observability into the job’s execution</p>
</span></td><td>Volatile</td></tr>
<tr><td><a name="crdb_internal.lease_holder"></a><code>crdb_internal.lease_holder(key: <a href="bytes.html">bytes</a>) &rarr; <a href="int.html">int</a></code></td><td><span class="funcdesc"><p>This function is used to fetch the leaseholder corresponding to a request key</p>
</span></td><td>Volatile</td></tr>
<tr><td><a name="crdb_internal.list_sql_keys_in_range"></a><code>crdb_internal.list_sql_keys_in_range(range_id: <a href="int.html">int</a>) &rarr; tuple{string AS key, string AS value, string AS ts}</code></td><td><span class="funcdesc"><p>Returns all SQL K/V pairs within the requested range.</p>
Expand Down
2 changes: 2 additions & 0 deletions pkg/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,7 @@ GO_TARGETS = [
"//pkg/jobs/jobsauth:jobsauth_test",
"//pkg/jobs/jobspb:jobspb",
"//pkg/jobs/jobspb:jobspb_test",
"//pkg/jobs/jobsprofiler/profilerconstants:profilerconstants",
"//pkg/jobs/jobsprofiler:jobsprofiler",
"//pkg/jobs/jobsprofiler:jobsprofiler_test",
"//pkg/jobs/jobsprotectedts:jobsprotectedts",
Expand Down Expand Up @@ -2682,6 +2683,7 @@ GET_X_DATA_TARGETS = [
"//pkg/jobs/jobsauth:get_x_data",
"//pkg/jobs/jobspb:get_x_data",
"//pkg/jobs/jobsprofiler:get_x_data",
"//pkg/jobs/jobsprofiler/profilerconstants:get_x_data",
"//pkg/jobs/jobsprotectedts:get_x_data",
"//pkg/jobs/jobstest:get_x_data",
"//pkg/jobs/metricspoller:get_x_data",
Expand Down
1 change: 1 addition & 0 deletions pkg/jobs/jobsprofiler/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ go_library(
deps = [
"//pkg/jobs",
"//pkg/jobs/jobspb",
"//pkg/jobs/jobsprofiler/profilerconstants",
"//pkg/sql",
"//pkg/sql/execinfrapb",
"//pkg/sql/isql",
Expand Down
9 changes: 5 additions & 4 deletions pkg/jobs/jobsprofiler/profiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/cockroachdb/cockroach/pkg/jobs"
"github.com/cockroachdb/cockroach/pkg/jobs/jobspb"
"github.com/cockroachdb/cockroach/pkg/jobs/jobsprofiler/profilerconstants"
"github.com/cockroachdb/cockroach/pkg/sql"
"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
"github.com/cockroachdb/cockroach/pkg/sql/isql"
Expand All @@ -37,15 +38,15 @@ func StorePlanDiagram(

err := db.Txn(ctx, func(ctx context.Context, txn isql.Txn) error {
flowSpecs := p.GenerateFlowSpecs()
_, diagURL, err := execinfrapb.GeneratePlanDiagramURL(fmt.Sprintf("job:%d", jobID), flowSpecs, execinfrapb.DiagramFlags{})
_, diagURL, err := execinfrapb.GeneratePlanDiagramURL(fmt.Sprintf("job:%d", jobID), flowSpecs,
execinfrapb.DiagramFlags{})
if err != nil {
return err
}

const infoKey = "dsp-diag-url-%d"
infoStorage := jobs.InfoStorageForJob(txn, jobID)
return infoStorage.Write(ctx, fmt.Sprintf(infoKey, timeutil.Now().UnixNano()),
[]byte(diagURL.String()))
return infoStorage.Write(ctx, fmt.Sprintf(profilerconstants.DSPDiagramInfoKeyPrefix+"%d",
timeutil.Now().UnixNano()), []byte(diagURL.String()))
})
if err != nil {
log.Warningf(ctx, "failed to generate and write DistSQL diagram for job %d: %v",
Expand Down
11 changes: 11 additions & 0 deletions pkg/jobs/jobsprofiler/profilerconstants/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
load("//build/bazelutil/unused_checker:unused.bzl", "get_x_data")
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "profilerconstants",
srcs = ["constants.go"],
importpath = "github.com/cockroachdb/cockroach/pkg/jobs/jobsprofiler/profilerconstants",
visibility = ["//visibility:public"],
)

get_x_data(name = "get_x_data")
15 changes: 15 additions & 0 deletions pkg/jobs/jobsprofiler/profilerconstants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2023 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// 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.

package profilerconstants

// DSPDiagramInfoKeyPrefix is the prefix of the info key used for rows that
// store the DistSQL plan diagram for a job.
const DSPDiagramInfoKeyPrefix = "dsp-diag-url-"
6 changes: 6 additions & 0 deletions pkg/sql/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ go_library(
"job_exec_context.go",
"job_exec_context_test_util.go",
"jobs_collection.go",
"jobs_execution_details.go",
"join.go",
"join_predicate.go",
"join_token.go",
Expand Down Expand Up @@ -309,6 +310,7 @@ go_library(
"//pkg/jobs",
"//pkg/jobs/jobsauth",
"//pkg/jobs/jobspb",
"//pkg/jobs/jobsprofiler/profilerconstants",
"//pkg/keys",
"//pkg/keyvisualizer",
"//pkg/kv",
Expand Down Expand Up @@ -636,6 +638,7 @@ go_test(
"indexbackfiller_test.go",
"instrumentation_test.go",
"internal_test.go",
"jobs_execution_details_test.go",
"join_token_test.go",
"main_test.go",
"materialized_view_test.go",
Expand Down Expand Up @@ -714,6 +717,7 @@ go_test(
deps = [
"//pkg/base",
"//pkg/build/bazel",
"//pkg/cloud/impl:cloudimpl",
"//pkg/clusterversion",
"//pkg/col/coldata",
"//pkg/config",
Expand All @@ -722,6 +726,8 @@ go_test(
"//pkg/internal/sqlsmith",
"//pkg/jobs",
"//pkg/jobs/jobspb",
"//pkg/jobs/jobsprofiler",
"//pkg/jobs/jobsprofiler/profilerconstants",
"//pkg/jobs/jobstest",
"//pkg/keys",
"//pkg/keyvisualizer",
Expand Down
1 change: 1 addition & 0 deletions pkg/sql/conn_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3398,6 +3398,7 @@ func (ex *connExecutor) initEvalCtx(ctx context.Context, evalCtx *extendedEvalCo
QueryCancelKey: ex.queryCancelKey,
DescIDGenerator: ex.getDescIDGenerator(),
RangeStatsFetcher: p.execCfg.RangeStatsFetcher,
JobsProfiler: p,
},
Tracing: &ex.sessionTracing,
MemMetrics: &ex.memMetrics,
Expand Down
51 changes: 32 additions & 19 deletions pkg/sql/delegate/show_jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,25 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry"
)

func (d *delegator) delegateShowJobs(n *tree.ShowJobs) (tree.Statement, error) {
if n.Schedules != nil {
// Limit the jobs displayed to the ones started by specified schedules.
return d.parse(fmt.Sprintf(`
SHOW JOBS SELECT id FROM system.jobs WHERE created_by_type='%s' and created_by_id IN (%s)
`, jobs.CreatedByScheduledJobs, n.Schedules.String()),
)
}

sqltelemetry.IncrementShowCounter(sqltelemetry.Jobs)

const (
selectClause = `
func constructSelectQuery(n *tree.ShowJobs) string {
var baseQuery strings.Builder
baseQuery.WriteString(`
SELECT job_id, job_type, description, statement, user_name, status,
running_status, created, started, finished, modified,
fraction_completed, error, coordinator_id, trace_id, last_run,
next_run, num_runs, execution_errors
FROM crdb_internal.jobs`
)
`)

// Check if there are any SHOW JOBS options that we need to add columns for.
if n.Options != nil {
if n.Options.ExecutionDetails {
baseQuery.WriteString(`, NULLIF(crdb_internal.job_execution_details(job_id)->>'plan_diagram'::STRING, '') AS plan_diagram`)
}
}

baseQuery.WriteString("\nFROM crdb_internal.jobs")

// Now add the predicates and ORDER BY clauses.
var typePredicate, whereClause, orderbyClause string
if n.Jobs == nil {
// Display all [only automatic] jobs without selecting specific jobs.
Expand Down Expand Up @@ -74,10 +74,23 @@ SELECT job_id, job_type, description, statement, user_name, status,
// Limit the jobs displayed to the select statement in n.Jobs.
whereClause = fmt.Sprintf(`WHERE job_id in (%s)`, n.Jobs.String())
}
return fmt.Sprintf("%s %s %s", baseQuery.String(), whereClause, orderbyClause)
}

func (d *delegator) delegateShowJobs(n *tree.ShowJobs) (tree.Statement, error) {
if n.Schedules != nil {
// Limit the jobs displayed to the ones started by specified schedules.
return d.parse(fmt.Sprintf(`
SHOW JOBS SELECT id FROM system.jobs WHERE created_by_type='%s' and created_by_id IN (%s)
`, jobs.CreatedByScheduledJobs, n.Schedules.String()),
)
}

sqltelemetry.IncrementShowCounter(sqltelemetry.Jobs)

sqlStmt := fmt.Sprintf("%s %s %s", selectClause, whereClause, orderbyClause)
stmt := constructSelectQuery(n)
if n.Block {
sqlStmt = fmt.Sprintf(
stmt = fmt.Sprintf(
`
WITH jobs AS (SELECT * FROM [%s]),
sleep_and_restart_if_unfinished AS (
Expand All @@ -92,7 +105,7 @@ SELECT job_id, job_type, description, statement, user_name, status,
)
SELECT *
FROM jobs
WHERE NOT EXISTS(SELECT * FROM fail_if_slept_too_long)`, sqlStmt)
WHERE NOT EXISTS(SELECT * FROM fail_if_slept_too_long)`, stmt)
}
return d.parse(sqlStmt)
return d.parse(stmt)
}
75 changes: 75 additions & 0 deletions pkg/sql/jobs_execution_details.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2023 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// 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.

package sql

import (
"context"
gojson "encoding/json"

"github.com/cockroachdb/cockroach/pkg/jobs"
"github.com/cockroachdb/cockroach/pkg/jobs/jobspb"
"github.com/cockroachdb/cockroach/pkg/jobs/jobsprofiler/profilerconstants"
"github.com/cockroachdb/cockroach/pkg/sql/isql"
"github.com/cockroachdb/cockroach/pkg/sql/sem/eval"
)

// GenerateExecutionDetailsJSON implements the Profiler interface.
func (p *planner) GenerateExecutionDetailsJSON(
ctx context.Context, evalCtx *eval.Context, jobID jobspb.JobID,
) ([]byte, error) {
execCfg := evalCtx.Planner.ExecutorConfig().(*ExecutorConfig)
j, err := execCfg.JobRegistry.LoadJob(ctx, jobID)
if err != nil {
return nil, err
}

var executionDetailsJSON []byte
payload := j.Payload()
switch payload.Type() {
// TODO(adityamaru): This allows different job types to implement custom
// execution detail aggregation.
default:
executionDetailsJSON, err = constructDefaultExecutionDetails(ctx, jobID, execCfg.InternalDB)
}

return executionDetailsJSON, err
}

// defaultExecutionDetails is a JSON serializable struct that captures the
// execution details that are not specific to a particular job type.
type defaultExecutionDetails struct {
// PlanDiagram is the URL to render the latest DistSQL plan that is being
// executed by the job.
PlanDiagram string `json:"plan_diagram"`
}

func constructDefaultExecutionDetails(
ctx context.Context, jobID jobspb.JobID, db isql.DB,
) ([]byte, error) {
executionDetails := &defaultExecutionDetails{}
err := db.Txn(ctx, func(ctx context.Context, txn isql.Txn) error {
// Populate the latest DSP diagram URL.
infoStorage := jobs.InfoStorageForJob(txn, jobID)
err := infoStorage.GetLast(ctx, profilerconstants.DSPDiagramInfoKeyPrefix, func(infoKey string, value []byte) error {
executionDetails.PlanDiagram = string(value)
return nil
})
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
j, err := gojson.Marshal(executionDetails)
return j, err
}
Loading

0 comments on commit 8124bff

Please sign in to comment.