// Copyright 2019 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 ( "bufio" "bytes" "context" "io" "os" "strings" "time" . "github.com/pingcap/check" "github.com/pingcap/failpoint" "github.com/pingcap/parser/terror" plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/mock" ) func parseLog(retriever *slowQueryRetriever, sctx sessionctx.Context, reader *bufio.Reader, logNum int) ([][]types.Datum, error) { retriever.parsedSlowLogCh = make(chan parsedSlowLog, 100) ctx := context.Background() retriever.parseSlowLog(ctx, sctx, reader, logNum) slowLog := <-retriever.parsedSlowLogCh rows, err := slowLog.rows, slowLog.err if err == io.EOF { err = nil } return rows, err } func parseSlowLog(sctx sessionctx.Context, reader *bufio.Reader, logNum int) ([][]types.Datum, error) { retriever := &slowQueryRetriever{} // Ignore the error is ok for test. terror.Log(retriever.initialize(context.Background(), sctx)) rows, err := parseLog(retriever, sctx, reader, logNum) return rows, err } func (s *testExecSuite) TestParseSlowLogPanic(c *C) { slowLogStr := `# Time: 2019-04-28T15:24:04.309074+08:00 # Txn_start_ts: 405888132465033227 # User@Host: root[root] @ localhost [127.0.0.1] # Query_time: 0.216905 # Cop_time: 0.38 Process_time: 0.021 Request_count: 1 Total_keys: 637 Processed_keys: 436 # Is_internal: true # Digest: 42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772 # Stats: t1:1,t2:2 # Cop_proc_avg: 0.1 Cop_proc_p90: 0.2 Cop_proc_max: 0.03 Cop_proc_addr: 127.0.0.1:20160 # Cop_wait_avg: 0.05 Cop_wait_p90: 0.6 Cop_wait_max: 0.8 Cop_wait_addr: 0.0.0.0:20160 # Mem_max: 70724 # Disk_max: 65536 # Plan_from_cache: true # Succ: false # Plan_digest: 60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4 # Prev_stmt: update t set i = 1; use test; select * from t;` c.Assert(failpoint.Enable("github.com/pingcap/tidb/executor/errorMockParseSlowLogPanic", `return(true)`), IsNil) defer func() { c.Assert(failpoint.Disable("github.com/pingcap/tidb/executor/errorMockParseSlowLogPanic"), IsNil) }() reader := bufio.NewReader(bytes.NewBufferString(slowLogStr)) loc, err := time.LoadLocation("Asia/Shanghai") c.Assert(err, IsNil) sctx := mock.NewContext() sctx.GetSessionVars().TimeZone = loc _, err = parseSlowLog(sctx, reader, 64) c.Assert(err, NotNil) c.Assert(err.Error(), Equals, "panic test") } func (s *testExecSuite) TestParseSlowLogFile(c *C) { slowLogStr := `# Time: 2019-04-28T15:24:04.309074+08:00 # Txn_start_ts: 405888132465033227 # User@Host: root[root] @ localhost [127.0.0.1] # Exec_retry_time: 0.12 Exec_retry_count: 57 # Query_time: 0.216905 # Cop_time: 0.38 Process_time: 0.021 Request_count: 1 Total_keys: 637 Processed_keys: 436 # Is_internal: true # Digest: 42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772 # Stats: t1:1,t2:2 # Cop_proc_avg: 0.1 Cop_proc_p90: 0.2 Cop_proc_max: 0.03 Cop_proc_addr: 127.0.0.1:20160 # Cop_wait_avg: 0.05 Cop_wait_p90: 0.6 Cop_wait_max: 0.8 Cop_wait_addr: 0.0.0.0:20160 # Cop_backoff_regionMiss_total_times: 200 Cop_backoff_regionMiss_total_time: 0.2 Cop_backoff_regionMiss_max_time: 0.2 Cop_backoff_regionMiss_max_addr: 127.0.0.1 Cop_backoff_regionMiss_avg_time: 0.2 Cop_backoff_regionMiss_p90_time: 0.2 # Cop_backoff_rpcPD_total_times: 200 Cop_backoff_rpcPD_total_time: 0.2 Cop_backoff_rpcPD_max_time: 0.2 Cop_backoff_rpcPD_max_addr: 127.0.0.1 Cop_backoff_rpcPD_avg_time: 0.2 Cop_backoff_rpcPD_p90_time: 0.2 # Cop_backoff_rpcTiKV_total_times: 200 Cop_backoff_rpcTiKV_total_time: 0.2 Cop_backoff_rpcTiKV_max_time: 0.2 Cop_backoff_rpcTiKV_max_addr: 127.0.0.1 Cop_backoff_rpcTiKV_avg_time: 0.2 Cop_backoff_rpcTiKV_p90_time: 0.2 # Mem_max: 70724 # Disk_max: 65536 # Plan_from_cache: true # Succ: false # Plan_digest: 60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4 # Prev_stmt: update t set i = 1; use test; select * from t;` reader := bufio.NewReader(bytes.NewBufferString(slowLogStr)) loc, err := time.LoadLocation("Asia/Shanghai") c.Assert(err, IsNil) ctx := mock.NewContext() ctx.GetSessionVars().TimeZone = loc rows, err := parseSlowLog(ctx, reader, 64) c.Assert(err, IsNil) c.Assert(len(rows), Equals, 1) recordString := "" for i, value := range rows[0] { str, err := value.ToString() c.Assert(err, IsNil) if i > 0 { recordString += "," } recordString += str } expectRecordString := `2019-04-28 15:24:04.309074,` + `405888132465033227,root,localhost,0,57,0.12,0.216905,` + `0,0,0,0,0,0,0,0,0,0,,0,0,0,0,0,0,0.38,0.021,0,0,0,1,637,0,,,1,42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772,t1:1,t2:2,` + `0.1,0.2,0.03,127.0.0.1:20160,0.05,0.6,0.8,0.0.0.0:20160,70724,65536,0,0,0,0,` + `Cop_backoff_regionMiss_total_times: 200 Cop_backoff_regionMiss_total_time: 0.2 Cop_backoff_regionMiss_max_time: 0.2 Cop_backoff_regionMiss_max_addr: 127.0.0.1 Cop_backoff_regionMiss_avg_time: 0.2 Cop_backoff_regionMiss_p90_time: 0.2 Cop_backoff_rpcPD_total_times: 200 Cop_backoff_rpcPD_total_time: 0.2 Cop_backoff_rpcPD_max_time: 0.2 Cop_backoff_rpcPD_max_addr: 127.0.0.1 Cop_backoff_rpcPD_avg_time: 0.2 Cop_backoff_rpcPD_p90_time: 0.2 Cop_backoff_rpcTiKV_total_times: 200 Cop_backoff_rpcTiKV_total_time: 0.2 Cop_backoff_rpcTiKV_max_time: 0.2 Cop_backoff_rpcTiKV_max_addr: 127.0.0.1 Cop_backoff_rpcTiKV_avg_time: 0.2 Cop_backoff_rpcTiKV_p90_time: 0.2,` + `0,0,1,,60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4,` + `update t set i = 1;,select * from t;` c.Assert(recordString, Equals, expectRecordString) // Issue 20928 reader = bufio.NewReader(bytes.NewBufferString(slowLogStr)) rows, err = parseSlowLog(ctx, reader, 1) c.Assert(err, IsNil) c.Assert(len(rows), Equals, 1) recordString = "" for i, value := range rows[0] { str, err := value.ToString() c.Assert(err, IsNil) if i > 0 { recordString += "," } recordString += str } c.Assert(expectRecordString, Equals, recordString) // fix sql contain '# ' bug slowLog := bytes.NewBufferString( `# Time: 2019-04-28T15:24:04.309074+08:00 select a# from t; # Time: 2019-01-24T22:32:29.313255+08:00 # Txn_start_ts: 405888132465033227 # Query_time: 0.216905 # Process_time: 0.021 Request_count: 1 Total_keys: 637 Processed_keys: 436 # Is_internal: true # Digest: 42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772 # Stats: t1:1,t2:2 # Succ: false select * from t; `) reader = bufio.NewReader(slowLog) _, err = parseSlowLog(ctx, reader, 64) c.Assert(err, IsNil) // test for time format compatibility. slowLog = bytes.NewBufferString( `# Time: 2019-04-28T15:24:04.309074+08:00 select * from t; # Time: 2019-04-24-19:41:21.716221 +0800 select * from t; `) reader = bufio.NewReader(slowLog) rows, err = parseSlowLog(ctx, reader, 64) c.Assert(err, IsNil) c.Assert(len(rows) == 2, IsTrue) t0Str, err := rows[0][0].ToString() c.Assert(err, IsNil) c.Assert(t0Str, Equals, "2019-04-28 15:24:04.309074") t1Str, err := rows[1][0].ToString() c.Assert(err, IsNil) c.Assert(t1Str, Equals, "2019-04-24 19:41:21.716221") // Add parse error check. slowLog = bytes.NewBufferString( `# Time: 2019-04-28T15:24:04.309074+08:00 # Succ: abc select * from t; `) reader = bufio.NewReader(slowLog) _, err = parseSlowLog(ctx, reader, 64) c.Assert(err, IsNil) warnings := ctx.GetSessionVars().StmtCtx.GetWarnings() c.Assert(warnings, HasLen, 1) c.Assert(warnings[0].Err.Error(), Equals, "Parse slow log at line 2 failed. Field: `Succ`, error: strconv.ParseBool: parsing \"abc\": invalid syntax") } // It changes variable.MaxOfMaxAllowedPacket, so must be stayed in SerialSuite. func (s *testExecSerialSuite) TestParseSlowLogFileSerial(c *C) { loc, err := time.LoadLocation("Asia/Shanghai") c.Assert(err, IsNil) ctx := mock.NewContext() ctx.GetSessionVars().TimeZone = loc // test for bufio.Scanner: token too long. slowLog := bytes.NewBufferString( `# Time: 2019-04-28T15:24:04.309074+08:00 select * from t; # Time: 2019-04-24-19:41:21.716221 +0800 `) originValue := variable.MaxOfMaxAllowedPacket variable.MaxOfMaxAllowedPacket = 65536 sql := strings.Repeat("x", int(variable.MaxOfMaxAllowedPacket+1)) slowLog.WriteString(sql) reader := bufio.NewReader(slowLog) _, err = parseSlowLog(ctx, reader, 64) c.Assert(err, NotNil) c.Assert(err.Error(), Equals, "single line length exceeds limit: 65536") variable.MaxOfMaxAllowedPacket = originValue reader = bufio.NewReader(slowLog) _, err = parseSlowLog(ctx, reader, 64) c.Assert(err, IsNil) } func (s *testExecSuite) TestSlowLogParseTime(c *C) { t1Str := "2019-01-24T22:32:29.313255+08:00" t2Str := "2019-01-24T22:32:29.313255" t1, err := ParseTime(t1Str) c.Assert(err, IsNil) loc, err := time.LoadLocation("Asia/Shanghai") c.Assert(err, IsNil) t2, err := time.ParseInLocation("2006-01-02T15:04:05.999999999", t2Str, loc) c.Assert(err, IsNil) c.Assert(t1.Unix(), Equals, t2.Unix()) t1Format := t1.In(loc).Format(logutil.SlowLogTimeFormat) c.Assert(t1Format, Equals, t1Str) } // TestFixParseSlowLogFile bugfix // sql select * from INFORMATION_SCHEMA.SLOW_QUERY limit 1; // ERROR 1105 (HY000): string "2019-05-12-11:23:29.61474688" doesn't has a prefix that matches format "2006-01-02-15:04:05.999999999 -0700", err: parsing time "2019-05-12-11:23:29.61474688" as "2006-01-02-15:04:05.999999999 -0700": cannot parse "" as "-0700" func (s *testExecSuite) TestFixParseSlowLogFile(c *C) { slowLog := bytes.NewBufferString( `# Time: 2019-05-12-11:23:29.614327491 +0800 # Txn_start_ts: 405888132465033227 # Query_time: 0.216905 # Process_time: 0.021 Request_count: 1 Total_keys: 637 Processed_keys: 436 # Is_internal: true # Digest: 42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772 # Stats: t1:1,t2:2 # Cop_proc_avg: 0.1 Cop_proc_p90: 0.2 Cop_proc_max: 0.03 # Cop_wait_avg: 0.05 Cop_wait_p90: 0.6 Cop_wait_max: 0.8 # Mem_max: 70724 select * from t # Time: 2019-05-12-11:23:29.614327491 +0800 # Txn_start_ts: 405888132465033227 # Query_time: 0.216905 # Process_time: 0.021 Request_count: 1 Total_keys: 637 Processed_keys: 436 # Is_internal: true # Digest: 42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772 # Stats: t1:1,t2:2 # Cop_proc_avg: 0.1 Cop_proc_p90: 0.2 Cop_proc_max: 0.03 # Cop_wait_avg: 0.05 Cop_wait_p90: 0.6 Cop_wait_max: 0.8 # Mem_max: 70724 # Plan_digest: 60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4 select * from t;`) scanner := bufio.NewReader(slowLog) loc, err := time.LoadLocation("Asia/Shanghai") c.Assert(err, IsNil) ctx := mock.NewContext() ctx.GetSessionVars().TimeZone = loc _, err = parseSlowLog(ctx, scanner, 64) c.Assert(err, IsNil) // Test parser error. slowLog = bytes.NewBufferString( `# Time: 2019-05-12-11:23:29.614327491 +0800 # Txn_start_ts: 405888132465033227# `) scanner = bufio.NewReader(slowLog) _, err = parseSlowLog(ctx, scanner, 64) c.Assert(err, IsNil) warnings := ctx.GetSessionVars().StmtCtx.GetWarnings() c.Assert(warnings, HasLen, 1) c.Assert(warnings[0].Err.Error(), Equals, "Parse slow log at line 2 failed. Field: `Txn_start_ts`, error: strconv.ParseUint: parsing \"405888132465033227#\": invalid syntax") } func (s *testExecSuite) TestSlowQueryRetriever(c *C) { writeFile := func(file string, data string) { f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY, 0644) c.Assert(err, IsNil) _, err = f.Write([]byte(data)) c.Assert(f.Close(), IsNil) c.Assert(err, IsNil) } logData0 := "" logData1 := ` # Time: 2020-02-15T18:00:01.000000+08:00 select 1; # Time: 2020-02-15T19:00:05.000000+08:00 select 2;` logData2 := ` # Time: 2020-02-16T18:00:01.000000+08:00 select 3; # Time: 2020-02-16T18:00:05.000000+08:00 select 4;` logData3 := ` # Time: 2020-02-16T19:00:00.000000+08:00 select 5; # Time: 2020-02-17T18:00:05.000000+08:00 select 6; # Time: 2020-04-15T18:00:05.299063744+08:00 select 7;` fileName0 := "tidb-slow-2020-02-14T19-04-05.01.log" fileName1 := "tidb-slow-2020-02-15T19-04-05.01.log" fileName2 := "tidb-slow-2020-02-16T19-04-05.01.log" fileName3 := "tidb-slow.log" writeFile(fileName0, logData0) writeFile(fileName1, logData1) writeFile(fileName2, logData2) writeFile(fileName3, logData3) defer func() { os.Remove(fileName0) os.Remove(fileName1) os.Remove(fileName2) os.Remove(fileName3) }() cases := []struct { startTime string endTime string files []string querys []string }{ { startTime: "2020-02-15T18:00:00.000000+08:00", endTime: "2020-02-17T20:00:00.000000+08:00", files: []string{fileName1, fileName2, fileName3}, querys: []string{ "select 1;", "select 2;", "select 3;", "select 4;", "select 5;", "select 6;", }, }, { startTime: "2020-02-15T18:00:02.000000+08:00", endTime: "2020-02-16T20:00:00.000000+08:00", files: []string{fileName1, fileName2, fileName3}, querys: []string{ "select 2;", "select 3;", "select 4;", "select 5;", }, }, { startTime: "2020-02-16T18:00:03.000000+08:00", endTime: "2020-02-16T18:59:00.000000+08:00", files: []string{fileName2}, querys: []string{ "select 4;", }, }, { startTime: "2020-02-16T18:00:03.000000+08:00", endTime: "2020-02-16T20:00:00.000000+08:00", files: []string{fileName2, fileName3}, querys: []string{ "select 4;", "select 5;", }, }, { startTime: "2020-02-16T19:00:00.000000+08:00", endTime: "2020-02-17T17:00:00.000000+08:00", files: []string{fileName3}, querys: []string{ "select 5;", }, }, { startTime: "2010-01-01T00:00:00.000000+08:00", endTime: "2010-01-01T01:00:00.000000+08:00", files: []string{}, }, { startTime: "2020-03-01T00:00:00.000000+08:00", endTime: "2010-03-01T01:00:00.000000+08:00", files: []string{}, }, { startTime: "", endTime: "", files: []string{fileName3}, querys: []string{ "select 5;", "select 6;", "select 7;", }, }, { startTime: "2020-04-15T18:00:05.299063744+08:00", endTime: "2020-04-15T18:00:05.299063744+08:00", files: []string{fileName3}, querys: []string{ "select 7;", }, }, } loc, err := time.LoadLocation("Asia/Shanghai") c.Assert(err, IsNil) sctx := mock.NewContext() sctx.GetSessionVars().TimeZone = loc sctx.GetSessionVars().SlowQueryFile = fileName3 for i, cas := range cases { extractor := &plannercore.SlowQueryExtractor{Enable: (len(cas.startTime) > 0 && len(cas.endTime) > 0)} if extractor.Enable { startTime, err := ParseTime(cas.startTime) c.Assert(err, IsNil) endTime, err := ParseTime(cas.endTime) c.Assert(err, IsNil) extractor.StartTime = startTime extractor.EndTime = endTime } retriever := &slowQueryRetriever{extractor: extractor} err := retriever.initialize(context.Background(), sctx) c.Assert(err, IsNil) comment := Commentf("case id: %v", i) c.Assert(retriever.files, HasLen, len(cas.files), comment) if len(retriever.files) > 0 { reader := bufio.NewReader(retriever.files[0].file) rows, err := parseLog(retriever, sctx, reader, 64) c.Assert(err, IsNil) c.Assert(len(rows), Equals, len(cas.querys), comment) for i, row := range rows { c.Assert(row[len(row)-1].GetString(), Equals, cas.querys[i], comment) } } for i, file := range retriever.files { c.Assert(file.file.Name(), Equals, cas.files[i]) c.Assert(file.file.Close(), IsNil) } } }