Skip to content
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

server: add http api to get some info of sub-optimal query #10717

Merged
merged 12 commits into from
Jun 24, 2019

Conversation

winoros
Copy link
Member

@winoros winoros commented Jun 5, 2019

What problem does this PR solve?

add HTTP api /sub-optimal-plan?pprof_time=&timeout=
to get some information of one SQL.
If we don't set pprof_time. We'll just get explain result. Otherwise we get explain analyze result.

What is changed and how it works?

Extract table names from SQL.
Dump the statistics of the tables.
Use show create table to get the schema of them.
Use Explain/Explain analyze to the result.
If we run explain analyze, we'll run pprof together to get CPU profile.

Check List

Tests

  • Manual test (use curl -X POST ... test it).

Related changes

  • Need to update the documentation
  • Need to be included in the release note

@winoros winoros added type/enhancement The issue or PR belongs to an enhancement. type/usability labels Jun 5, 2019
terror.Log(err)
}

sleep := func(w http.ResponseWriter, d time.Duration) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Deprecated: the CloseNotifier interface predates Go's context package.
// New code should use Request.Context instead.

jsonTbl, err := sh.getStatsForTable(pair)
if err != nil {
terror.Log(err)
continue
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we continue or just throw error out?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we return a message to indicate the failure of dumping stats for this table?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write a file like dbname.tblname.stats.err?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks OK to me

@codecov
Copy link

codecov bot commented Jun 5, 2019

Codecov Report

Merging #10717 into master will not change coverage.
The diff coverage is n/a.

@@             Coverage Diff             @@
##             master     #10717   +/-   ##
===========================================
  Coverage   80.9431%   80.9431%           
===========================================
  Files           420        420           
  Lines         89506      89506           
===========================================
  Hits          72449      72449           
  Misses        11829      11829           
  Partials       5228       5228

func (sh *sqlInfoFetcher) zipInfoForSQL(w http.ResponseWriter, r *http.Request) {
reqCtx := r.Context()
sql := r.FormValue("sql")
pprofTimeString := r.FormValue("pprof_time")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about profile_sec?

return
}
if pprofTime < 5 {
serveError(w, http.StatusBadRequest, "pprof time is too short")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a best practice for this kind of error message is:

  1. point out the invalid value
  2. point out the valid value range

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we return here if it's an invalid value?

}
pairs, err := sh.extractTableNames(sql)
if err != nil {
serveError(w, http.StatusBadRequest, fmt.Sprintf("invalid SQL text, err: %v", err))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about "failed to extract table names, sql: %v"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the sql is something given by the user.

jsonTbl, err := sh.getStatsForTable(pair)
if err != nil {
terror.Log(err)
continue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we return a message to indicate the failure of dumping stats for this table?

server/sql_info_fetcher.go Outdated Show resolved Hide resolved
server/sql_info_fetcher.go Show resolved Hide resolved
@winoros winoros added the priority/P1 The issue has P1 priority. label Jun 6, 2019
for pair := range pairs {
err = sh.getShowCreateTable(pair, zw)
if err != nil {
serveError(w, http.StatusInternalServerError, fmt.Sprintf("get schema for %v.%v failed", pair.DBName, pair.TableName))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ok that we write error messages and zip formatted data to w at the same time?

Copy link
Member Author

@winoros winoros Jun 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, it's not very ok. The following is the curl result when error arisen.

➜  tidb git:(http-fetch-sql-info) ✗ curl -X POST http://127.0.0.1:10080/debug/sub-optimal-plan -d "sql=select%20*%20from%20t&current_db=test"
execute SQL failed, err: [planner:1046]No database selected
PK
test.t.json��Aj�0���Z
                     ��i��� [iDe+X������"=C��a��[h�Ϋ?���� V4#��陉�6����Y��Ƣ�}��2
u�B��P�:��Ω��t�⧤���~���ܣ&�Ѭ��ɕ�A�E�t�wc�y�k�m�zl6��aw|�yC����H�؆"v�F�+����ן�7?j\���u�O��P� "f���Ӡa\�����T
XQtest.t.schema.txt*�tru
                        qqt�qUH(IP��RPH�LP(K,J�H,�02�Tpqus
                                                          �	Q�
                                                                  ����Tp�s��s�����wq�K:{8��ؖ��Y�&�(8���8�����I�y\���P(%��ip� "f
XQ
  test.t.json(%��ipCtest.t.schema.txtPKx�%

But the api debug/zip did the same thing. #9651
Or we may need to write to a BytesBuffer first then write to ResponseWriter

Copy link
Contributor

@qw4990 qw4990 Jun 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK... for simplifying, we can leave it as a further optimization and do it in another PR and please add a TODO here.

timeoutString := r.FormValue("timeout")
curDB := strings.ToLower(r.FormValue("current_db"))
if curDB != "" {
_, err = sh.s.Execute(reqCtx, fmt.Sprintf("use %v", curDB))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_, err = sh.s.Execute(reqCtx, fmt.Sprintf("use %v", curDB))
_, err = sh.s.Execute(reqCtx, "use " + curDB)

if curDB != "" {
_, err = sh.s.Execute(reqCtx, fmt.Sprintf("use %v", curDB))
if err != nil {
serveError(w, http.StatusInternalServerError, fmt.Sprintf("use database failed, err: %v", err))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
serveError(w, http.StatusInternalServerError, fmt.Sprintf("use database failed, err: %v", err))
serveError(w, http.StatusInternalServerError, fmt.Sprintf("use database %v failed: %v", curDB , err))

if pprofTimeString != "" {
pprofTime, err = strconv.Atoi(pprofTimeString)
}
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check err in the above if statement

if timeoutString != "" {
timeout, err = strconv.Atoi(timeoutString)
}
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

jsonTbl, err := sh.getStatsForTable(pair)
if err != nil {
terror.Log(err)
continue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks OK to me

curDB: curDB,
names: make(map[tableNamePair]struct{}),
}
for _, stmt := range stmts {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we allow multiple statements? Will it cause problems if we use explain sql?

Copy link
Member

@zz-jason zz-jason left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@zz-jason zz-jason added the status/LGT1 Indicates that a PR has LGTM 1. label Jun 24, 2019
Copy link
Contributor

@alivxxx alivxxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@alivxxx
Copy link
Contributor

alivxxx commented Jun 24, 2019

/run-all-tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority/P1 The issue has P1 priority. status/LGT1 Indicates that a PR has LGTM 1. type/enhancement The issue or PR belongs to an enhancement. type/usability
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants