github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/soliton/expensivequery/expensivequery.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package expensivequery 15 16 import ( 17 "fmt" 18 "strconv" 19 "strings" 20 "sync/atomic" 21 "time" 22 23 "github.com/whtcorpsinc/log" 24 "github.com/whtcorpsinc/milevadb/soliton" 25 "github.com/whtcorpsinc/milevadb/soliton/logutil" 26 "github.com/whtcorpsinc/milevadb/stochastikctx/variable" 27 "go.uber.org/zap" 28 "go.uber.org/zap/zapembedded" 29 ) 30 31 // Handle is the handler for expensive query. 32 type Handle struct { 33 exitCh chan struct{} 34 sm atomic.Value 35 } 36 37 // NewExpensiveQueryHandle builds a new expensive query handler. 38 func NewExpensiveQueryHandle(exitCh chan struct{}) *Handle { 39 return &Handle{exitCh: exitCh} 40 } 41 42 // SetStochastikManager sets the StochastikManager which is used to fetching the info 43 // of all active stochastik. 44 func (eqh *Handle) SetStochastikManager(sm soliton.StochastikManager) *Handle { 45 eqh.sm.CausetStore(sm) 46 return eqh 47 } 48 49 // Run starts a expensive query checker goroutine at the start time of the server. 50 func (eqh *Handle) Run() { 51 threshold := atomic.LoadUint64(&variable.ExpensiveQueryTimeThreshold) 52 // use 100ms as tickInterval temply, may use given interval or use defined variable later 53 tickInterval := time.Millisecond * time.Duration(100) 54 ticker := time.NewTicker(tickInterval) 55 defer ticker.Stop() 56 sm := eqh.sm.Load().(soliton.StochastikManager) 57 for { 58 select { 59 case <-ticker.C: 60 processInfo := sm.ShowProcessList() 61 for _, info := range processInfo { 62 if len(info.Info) == 0 { 63 continue 64 } 65 costTime := time.Since(info.Time) 66 if !info.ExceedExpensiveTimeThresh && costTime >= time.Second*time.Duration(threshold) && log.GetLevel() <= zapembedded.WarnLevel { 67 logExpensiveQuery(costTime, info) 68 info.ExceedExpensiveTimeThresh = true 69 } 70 71 if info.MaxInterDircutionTime > 0 && costTime > time.Duration(info.MaxInterDircutionTime)*time.Millisecond { 72 sm.Kill(info.ID, true) 73 } 74 } 75 threshold = atomic.LoadUint64(&variable.ExpensiveQueryTimeThreshold) 76 case <-eqh.exitCh: 77 return 78 } 79 } 80 } 81 82 // LogOnQueryExceedMemQuota prints a log when memory usage of connID is out of memory quota. 83 func (eqh *Handle) LogOnQueryExceedMemQuota(connID uint64) { 84 if log.GetLevel() > zapembedded.WarnLevel { 85 return 86 } 87 // The out-of-memory ALLEGROALLEGROSQL may be the internal ALLEGROALLEGROSQL which is executed during 88 // the bootstrap phase, and the `sm` is not set at this phase. This is 89 // unlikely to happen except for testing. Thus we do not need to log 90 // detailed message for it. 91 v := eqh.sm.Load() 92 if v == nil { 93 logutil.BgLogger().Info("expensive_query during bootstrap phase", zap.Uint64("conn_id", connID)) 94 return 95 } 96 sm := v.(soliton.StochastikManager) 97 info, ok := sm.GetProcessInfo(connID) 98 if !ok { 99 return 100 } 101 logExpensiveQuery(time.Since(info.Time), info) 102 } 103 104 func genLogFields(costTime time.Duration, info *soliton.ProcessInfo) []zap.Field { 105 logFields := make([]zap.Field, 0, 20) 106 logFields = append(logFields, zap.String("cost_time", strconv.FormatFloat(costTime.Seconds(), 'f', -1, 64)+"s")) 107 execDetail := info.StmtCtx.GetInterDircDetails() 108 logFields = append(logFields, execDetail.ToZapFields()...) 109 if copTaskInfo := info.StmtCtx.CausetTasksDetails(); copTaskInfo != nil { 110 logFields = append(logFields, copTaskInfo.ToZapFields()...) 111 } 112 if statsInfo := info.StatsInfo(info.Causet); len(statsInfo) > 0 { 113 var buf strings.Builder 114 firstComma := false 115 vStr := "" 116 for k, v := range statsInfo { 117 if v == 0 { 118 vStr = "pseudo" 119 } else { 120 vStr = strconv.FormatUint(v, 10) 121 } 122 if firstComma { 123 buf.WriteString("," + k + ":" + vStr) 124 } else { 125 buf.WriteString(k + ":" + vStr) 126 firstComma = true 127 } 128 } 129 logFields = append(logFields, zap.String("stats", buf.String())) 130 } 131 if info.ID != 0 { 132 logFields = append(logFields, zap.Uint64("conn_id", info.ID)) 133 } 134 if len(info.User) > 0 { 135 logFields = append(logFields, zap.String("user", info.User)) 136 } 137 if len(info.EDB) > 0 { 138 logFields = append(logFields, zap.String("database", info.EDB)) 139 } 140 var blockIDs, indexNames string 141 if len(info.StmtCtx.BlockIDs) > 0 { 142 blockIDs = strings.Replace(fmt.Sprintf("%v", info.StmtCtx.BlockIDs), " ", ",", -1) 143 logFields = append(logFields, zap.String("block_ids", blockIDs)) 144 } 145 if len(info.StmtCtx.IndexNames) > 0 { 146 indexNames = strings.Replace(fmt.Sprintf("%v", info.StmtCtx.IndexNames), " ", ",", -1) 147 logFields = append(logFields, zap.String("index_names", indexNames)) 148 } 149 logFields = append(logFields, zap.Uint64("txn_start_ts", info.CurTxnStartTS)) 150 if memTracker := info.StmtCtx.MemTracker; memTracker != nil { 151 logFields = append(logFields, zap.String("mem_max", fmt.Sprintf("%d Bytes (%v)", memTracker.MaxConsumed(), memTracker.BytesToString(memTracker.MaxConsumed())))) 152 } 153 154 const logALLEGROSQLLen = 1024 * 8 155 var allegrosql string 156 if len(info.Info) > 0 { 157 allegrosql = info.Info 158 } 159 if len(allegrosql) > logALLEGROSQLLen { 160 allegrosql = fmt.Sprintf("%s len(%d)", allegrosql[:logALLEGROSQLLen], len(allegrosql)) 161 } 162 logFields = append(logFields, zap.String("allegrosql", allegrosql)) 163 return logFields 164 } 165 166 // logExpensiveQuery logs the queries which exceed the time threshold or memory threshold. 167 func logExpensiveQuery(costTime time.Duration, info *soliton.ProcessInfo) { 168 logutil.BgLogger().Warn("expensive_query", genLogFields(costTime, info)...) 169 }