@@ -42,8 +42,11 @@ import (
4242 "github.com/cockroachdb/cockroach/pkg/util/leaktest"
4343 "github.com/cockroachdb/cockroach/pkg/util/log"
4444 "github.com/cockroachdb/cockroach/pkg/util/protoutil"
45+ "github.com/cockroachdb/cockroach/pkg/util/tracing"
4546 "github.com/cockroachdb/cockroach/pkg/util/uuid"
4647 "github.com/cockroachdb/errors"
48+ "github.com/gogo/protobuf/types"
49+ "github.com/klauspost/compress/zip"
4750 "github.com/stretchr/testify/require"
4851)
4952
@@ -224,6 +227,80 @@ func TestReadWriteProfilerExecutionDetails(t *testing.T) {
224227 t .Run ("execution details for invalid job ID" , func (t * testing.T ) {
225228 runner .ExpectErr (t , `job -123 not found; cannot request execution details` , `SELECT crdb_internal.request_job_execution_details(-123)` )
226229 })
230+
231+ t .Run ("read/write terminal trace" , func (t * testing.T ) {
232+ jobs .RegisterConstructor (jobspb .TypeImport , func (j * jobs.Job , _ * cluster.Settings ) jobs.Resumer {
233+ return fakeExecResumer {
234+ OnResume : func (ctx context.Context ) error {
235+ sp := tracing .SpanFromContext (ctx )
236+ require .NotNil (t , sp )
237+ sp .RecordStructured (& types.StringValue {Value : "should see this" })
238+ return nil
239+ },
240+ }
241+ }, jobs .UsesTenantCostControl )
242+ var importJobID int
243+ runner .QueryRow (t , `IMPORT INTO t CSV DATA ('nodelocal://1/foo') WITH DETACHED` ).Scan (& importJobID )
244+ jobutils .WaitForJobToSucceed (t , runner , jobspb .JobID (importJobID ))
245+ runner .Exec (t , `SELECT crdb_internal.request_job_execution_details($1)` , importJobID )
246+ trace := checkExecutionDetails (t , s , jobspb .JobID (importJobID ), "resumer-trace" )
247+ require .Contains (t , string (trace ), "should see this" )
248+ })
249+
250+ t .Run ("read/write active trace" , func (t * testing.T ) {
251+ blockCh := make (chan struct {})
252+ continueCh := make (chan struct {})
253+ defer close (blockCh )
254+ defer close (continueCh )
255+ jobs .RegisterConstructor (jobspb .TypeImport , func (j * jobs.Job , _ * cluster.Settings ) jobs.Resumer {
256+ return fakeExecResumer {
257+ OnResume : func (ctx context.Context ) error {
258+ _ , childSp := tracing .ChildSpan (ctx , "child" )
259+ defer childSp .Finish ()
260+ blockCh <- struct {}{}
261+ <- continueCh
262+ return nil
263+ },
264+ }
265+ }, jobs .UsesTenantCostControl )
266+ var importJobID int
267+ runner .QueryRow (t , `IMPORT INTO t CSV DATA ('nodelocal://1/foo') WITH DETACHED` ).Scan (& importJobID )
268+ <- blockCh
269+ runner .Exec (t , `SELECT crdb_internal.request_job_execution_details($1)` , importJobID )
270+ activeTraces := checkExecutionDetails (t , s , jobspb .JobID (importJobID ), "trace" )
271+ continueCh <- struct {}{}
272+ jobutils .WaitForJobToSucceed (t , runner , jobspb .JobID (importJobID ))
273+ unzip , err := zip .NewReader (bytes .NewReader (activeTraces ), int64 (len (activeTraces )))
274+ require .NoError (t , err )
275+
276+ // Make sure the bundle contains the expected list of files.
277+ var files []string
278+ for _ , f := range unzip .File {
279+ if f .UncompressedSize64 == 0 {
280+ t .Fatalf ("file %s is empty" , f .Name )
281+ }
282+ files = append (files , f .Name )
283+
284+ r , err := f .Open ()
285+ if err != nil {
286+ t .Fatal (err )
287+ }
288+ defer r .Close ()
289+ bytes , err := io .ReadAll (r )
290+ if err != nil {
291+ t .Fatal (err )
292+ }
293+ contents := string (bytes )
294+
295+ // Verify some contents in the active traces.
296+ if strings .Contains (f .Name , ".txt" ) {
297+ require .Regexp (t , "[child: {count: 1, duration.*, unfinished}]" , contents )
298+ } else if strings .Contains (f .Name , ".json" ) {
299+ require .True (t , strings .Contains (contents , "\" operationName\" : \" child\" " ))
300+ }
301+ }
302+ require .Equal (t , []string {"node1-trace.txt" , "node1-jaeger.json" }, files )
303+ })
227304}
228305
229306func TestListProfilerExecutionDetails (t * testing.T ) {
@@ -272,10 +349,11 @@ func TestListProfilerExecutionDetails(t *testing.T) {
272349
273350 runner .Exec (t , `SELECT crdb_internal.request_job_execution_details($1)` , importJobID )
274351 files := listExecutionDetails (t , s , jobspb .JobID (importJobID ))
275- require .Len (t , files , 3 )
352+ require .Len (t , files , 4 )
276353 require .Regexp (t , "distsql\\ ..*\\ .html" , files [0 ])
277354 require .Regexp (t , "goroutines\\ ..*\\ .txt" , files [1 ])
278355 require .Regexp (t , "resumer-trace-n[0-9]\\ ..*\\ .txt" , files [2 ])
356+ require .Regexp (t , "trace\\ ..*\\ .zip" , files [3 ])
279357
280358 // Resume the job, so it can write another DistSQL diagram and goroutine
281359 // snapshot.
@@ -285,13 +363,15 @@ func TestListProfilerExecutionDetails(t *testing.T) {
285363 jobutils .WaitForJobToSucceed (t , runner , jobspb .JobID (importJobID ))
286364 runner .Exec (t , `SELECT crdb_internal.request_job_execution_details($1)` , importJobID )
287365 files = listExecutionDetails (t , s , jobspb .JobID (importJobID ))
288- require .Len (t , files , 6 )
366+ require .Len (t , files , 8 )
289367 require .Regexp (t , "distsql\\ ..*\\ .html" , files [0 ])
290368 require .Regexp (t , "distsql\\ ..*\\ .html" , files [1 ])
291369 require .Regexp (t , "goroutines\\ ..*\\ .txt" , files [2 ])
292370 require .Regexp (t , "goroutines\\ ..*\\ .txt" , files [3 ])
293371 require .Regexp (t , "resumer-trace-n[0-9]\\ ..*\\ .txt" , files [4 ])
294372 require .Regexp (t , "resumer-trace-n[0-9]\\ ..*\\ .txt" , files [5 ])
373+ require .Regexp (t , "trace\\ ..*\\ .zip" , files [6 ])
374+ require .Regexp (t , "trace\\ ..*\\ .zip" , files [7 ])
295375 })
296376}
297377
0 commit comments