-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
*: tidb tracing prototype #7016
Changes from 22 commits
a8821c8
92d1764
3e61f1b
93b420a
b8abe7e
7a9d6db
b46a727
84b9d78
3fe6951
31961e6
c757655
70f0d9b
6181ca2
cb366aa
46e681e
e0d7cba
7657e5e
227626b
03c3f7a
507b567
0ccae95
e2cb6ff
9f5b5bf
e49d58a
da0e06b
2c6aa09
1468f03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,6 +89,8 @@ func (b *executorBuilder) build(p plan.Plan) Executor { | |
return b.buildDelete(v) | ||
case *plan.Execute: | ||
return b.buildExecute(v) | ||
case *plan.Trace: | ||
return b.buildTrace(v) | ||
case *plan.Explain: | ||
return b.buildExplain(v) | ||
case *plan.PointGetPlan: | ||
|
@@ -619,6 +621,16 @@ func (b *executorBuilder) buildDDL(v *plan.DDL) Executor { | |
return e | ||
} | ||
|
||
// buildTrace builds a TraceExec for future executing. This method will be called | ||
// at build(). | ||
func (b *executorBuilder) buildTrace(v *plan.Trace) Executor { | ||
return &TraceExec{ | ||
baseExecutor: newBaseExecutor(b.ctx, v.Schema(), v.ExplainID()), | ||
stmtNode: v.StmtNode, | ||
builder: b, | ||
} | ||
} | ||
|
||
// buildExplain builds a explain executor. `e.rows` collects final result to `ExplainExec`. | ||
func (b *executorBuilder) buildExplain(v *plan.Explain) Executor { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move code makes a reviewer waste more time to view a PR There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
e := &ExplainExec{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,9 +57,6 @@ type TableReaderExecutor struct { | |
|
||
// Open initialzes necessary variables for using this executor. | ||
func (e *TableReaderExecutor) Open(ctx context.Context) error { | ||
span, ctx := startSpanFollowsContext(ctx, "executor.TableReader.Open") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why remove this? |
||
defer span.Finish() | ||
|
||
var err error | ||
if e.corColInFilter { | ||
e.dagPB.Executors, _, err = constructDistExec(e.ctx, e.plans) | ||
|
@@ -101,11 +98,11 @@ func (e *TableReaderExecutor) Open(ctx context.Context) error { | |
// Next fills data into the chunk passed by its caller. | ||
// The task was actually done by tableReaderHandler. | ||
func (e *TableReaderExecutor) Next(ctx context.Context, chk *chunk.Chunk) error { | ||
err := e.resultHandler.nextChunk(ctx, chk) | ||
if err != nil { | ||
if err := e.resultHandler.nextChunk(ctx, chk); err != nil { | ||
e.feedback.Invalidate() | ||
return err | ||
} | ||
return errors.Trace(err) | ||
return errors.Trace(nil) | ||
} | ||
|
||
// Close implements the Executor Close interface. | ||
|
@@ -115,7 +112,7 @@ func (e *TableReaderExecutor) Close() error { | |
return errors.Trace(err) | ||
} | ||
|
||
// buildResp first build request and send it to tikv using distsql.Select. It uses SelectResut returned by the callee | ||
// buildResp first builds request and sends it to tikv using distsql.Select. It uses SelectResut returned by the callee | ||
// to fetch all results. | ||
func (e *TableReaderExecutor) buildResp(ctx context.Context, ranges []*ranger.Range) (distsql.SelectResult, error) { | ||
var builder distsql.RequestBuilder | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// Copyright 2018 PingCAP, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package executor | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/juju/errors" | ||
"github.com/opentracing/basictracer-go" | ||
opentracing "github.com/opentracing/opentracing-go" | ||
"github.com/pingcap/tidb/ast" | ||
"github.com/pingcap/tidb/plan" | ||
"github.com/pingcap/tidb/util/chunk" | ||
"github.com/pingcap/tidb/util/tracing" | ||
"golang.org/x/net/context" | ||
) | ||
|
||
// TraceExec represents a root executor of trace query. | ||
type TraceExec struct { | ||
baseExecutor | ||
// CollectedSpans collects all span during execution. Span is appended via | ||
// callback method which passes into tracer implementation. | ||
CollectedSpans []basictracer.RawSpan | ||
// exhausted being true means there is no more result. | ||
exhausted bool | ||
zhexuany marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// stmtNode is the real query ast tree and it is used for building real query's plan. | ||
stmtNode ast.StmtNode | ||
// rootTrace represents root span which is father of all other span. | ||
rootTrace opentracing.Span | ||
|
||
childrenResults []*chunk.Chunk | ||
|
||
zhexuany marked this conversation as resolved.
Show resolved
Hide resolved
|
||
builder *executorBuilder | ||
} | ||
|
||
// Open opens a trace executor and it will create a root trace span which will be | ||
// used for the following span in a relationship of `ChildOf` or `FollowFrom`. | ||
// for more details, you could refer to http://opentracing.io | ||
func (e *TraceExec) Open(ctx context.Context) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems that we can not trace the building and optimization time of a query, only the execution time can be traced? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is only for now. I will file another PR to support this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the |
||
e.rootTrace = tracing.NewRecordedTrace("trace_exec", func(sp basictracer.RawSpan) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After that, global opentracing Tracer is modified? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I will file another PR to better handle this. |
||
e.CollectedSpans = append(e.CollectedSpans, sp) | ||
}) | ||
// we actually don't care when underlying executor started. We only care how | ||
// much time was spent | ||
for _, child := range e.children { | ||
err := child.Open(ctx) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will some node open multiple times? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Usually, it is not. I just copied the code from basicExecutor with some addition about how to set up tracing. |
||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
} | ||
e.childrenResults = make([]*chunk.Chunk, 0, len(e.children)) | ||
for _, child := range e.children { | ||
e.childrenResults = append(e.childrenResults, child.newChunk()) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Next executes real query and collects span later. | ||
func (e *TraceExec) Next(ctx context.Context, chk *chunk.Chunk) error { | ||
chk.Reset() | ||
if e.exhausted { | ||
return nil | ||
} | ||
|
||
// record how much time was spent for optimizeing plan | ||
optimizeSp := e.rootTrace.Tracer().StartSpan("plan_optimize", opentracing.FollowsFrom(e.rootTrace.Context())) | ||
stmtPlan, err := plan.Optimize(e.builder.ctx, e.stmtNode, e.builder.is) | ||
if err != nil { | ||
return err | ||
} | ||
optimizeSp.Finish() | ||
pp, _ := stmtPlan.(plan.PhysicalPlan) | ||
for _, child := range pp.Children() { | ||
switch p := child.(type) { | ||
case *plan.PhysicalTableReader, *plan.PhysicalIndexReader, *plan.PhysicalIndexLookUpReader, *plan.PhysicalHashAgg, *plan.PhysicalProjection, *plan.PhysicalStreamAgg, *plan.PhysicalSort: | ||
e.children = append(e.children, e.builder.build(p)) | ||
default: | ||
return errors.Errorf("%v is not supported", child) | ||
} | ||
} | ||
|
||
// store span into context | ||
ctx = opentracing.ContextWithSpan(ctx, e.rootTrace) | ||
|
||
if len(e.children) > 0 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can be removed. |
||
for { | ||
if err := e.children[0].Next(ctx, e.childrenResults[0]); err != nil { | ||
return errors.Trace(err) | ||
} | ||
if e.childrenResults[0].NumRows() != 0 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
break | ||
} | ||
} | ||
} | ||
|
||
e.rootTrace.LogKV("event", "tracing completed") | ||
e.rootTrace.Finish() | ||
var rootSpan basictracer.RawSpan | ||
|
||
treeSpans := make(map[uint64][]basictracer.RawSpan) | ||
for _, sp := range e.CollectedSpans { | ||
treeSpans[sp.ParentSpanID] = append(treeSpans[sp.ParentSpanID], sp) | ||
// if a span's parentSpanID is 0, then it is root span | ||
// this is by design | ||
if sp.ParentSpanID == 0 { | ||
rootSpan = sp | ||
} | ||
} | ||
|
||
dfsTree(rootSpan, treeSpans, "", false, chk) | ||
e.exhausted = true | ||
return nil | ||
} | ||
|
||
func dfsTree(span basictracer.RawSpan, tree map[uint64][]basictracer.RawSpan, prefix string, isLast bool, chk *chunk.Chunk) { | ||
suffix := "" | ||
spans := tree[span.Context.SpanID] | ||
var newPrefix string | ||
if span.ParentSpanID == 0 { | ||
newPrefix = prefix | ||
} else { | ||
if len(tree[span.ParentSpanID]) > 0 && !isLast { | ||
suffix = "├─" | ||
newPrefix = prefix + "│ " | ||
} else { | ||
suffix = "└─" | ||
newPrefix = prefix + " " | ||
} | ||
} | ||
|
||
chk.AppendString(0, prefix+suffix+span.Operation) | ||
chk.AppendString(1, span.Start.Format(time.StampNano)) | ||
chk.AppendString(2, span.Duration.String()) | ||
|
||
for i, sp := range spans { | ||
dfsTree(sp, tree, newPrefix, i == (len(spans))-1 /*last element of array*/, chk) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright 2018 PingCAP, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package executor_test | ||
|
||
import ( | ||
. "github.com/pingcap/check" | ||
"github.com/pingcap/tidb/util/testkit" | ||
) | ||
|
||
type testTraceExec struct{} | ||
|
||
func (s *testTraceExec) SetupSuite(c *C) { | ||
} | ||
|
||
func (s *testSuite) TestTraceExec(c *C) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems this test file does nothing, can we remove the whole file and add it when adding ut. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I leave a todo and I will soon add the actual test in another PR. |
||
tk := testkit.NewTestKit(c, s.store) | ||
tk.MustExec("use test") | ||
testSQL := `create table trace (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1);` | ||
tk.MustExec(testSQL) | ||
// TODO: check result later in another PR. | ||
tk.MustExec("trace select * from trace where id = 0;") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package plan | ||
|
||
import ( | ||
"github.com/pingcap/tidb/ast" | ||
) | ||
|
||
// Trace represents a trace plan. | ||
type Trace struct { | ||
baseSchemaProducer | ||
|
||
StmtNode ast.StmtNode | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we store the plain text of the
select
statement to be traced? We can trace the time spent by parser with the help of this.