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  }