github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/interlock/inspection_profile.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 interlock
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"fmt"
    20  	"math"
    21  	"strconv"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/whtcorpsinc/milevadb/schemareplicant"
    26  	"github.com/whtcorpsinc/milevadb/soliton/sqlexec"
    27  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    28  )
    29  
    30  const (
    31  	dateTimeFormat = "2006-01-02 15:04:05"
    32  )
    33  
    34  type profileBuilder struct {
    35  	sctx            stochastikctx.Context
    36  	idMap           map[string]uint64
    37  	idSlabPredictor uint64
    38  	totalValue      float64
    39  	uniqueMap       map[string]struct{}
    40  	buf             *bytes.Buffer
    41  	start           time.Time
    42  	end             time.Time
    43  	valueTP         metricValueType
    44  }
    45  
    46  type metricNode struct {
    47  	causet     string
    48  	name       string
    49  	label      []string
    50  	condition  string
    51  	labelValue map[string]*metricValue
    52  	value      *metricValue
    53  	unit       int64
    54  	children   []*metricNode
    55  	// isPartOfParent indicates the parent of this node not fully contain this node.
    56  	isPartOfParent bool
    57  	initialized    bool
    58  }
    59  
    60  type metricValue struct {
    61  	sum     float64
    62  	count   int
    63  	avgP99  float64
    64  	avgP90  float64
    65  	avgP80  float64
    66  	comment string
    67  }
    68  
    69  type metricValueType int
    70  
    71  const (
    72  	metricValueSum metricValueType = iota + 1
    73  	metricValueAvg
    74  	metricValueCnt
    75  )
    76  
    77  func (m metricValueType) String() string {
    78  	switch m {
    79  	case metricValueAvg:
    80  		return "avg"
    81  	case metricValueCnt:
    82  		return "count"
    83  	default:
    84  		return "sum"
    85  	}
    86  }
    87  
    88  func (m *metricValue) getValue(tp metricValueType) float64 {
    89  	timeValue := 0.0
    90  	switch tp {
    91  	case metricValueCnt:
    92  		return float64(m.count)
    93  	case metricValueSum:
    94  		timeValue = m.sum
    95  	case metricValueAvg:
    96  		if m.count == 0 {
    97  			return 0.0
    98  		}
    99  		timeValue = m.sum / float64(m.count)
   100  	default:
   101  		panic("should never happen")
   102  	}
   103  	if math.IsNaN(timeValue) {
   104  		return 0
   105  	}
   106  	return timeValue
   107  }
   108  
   109  func (m *metricValue) getComment() string {
   110  	if m.count == 0 {
   111  		return ""
   112  	}
   113  	buf := bytes.NewBuffer(make([]byte, 0, 64))
   114  	buf.WriteString(m.comment)
   115  	buf.WriteString("\n\n")
   116  	buf.WriteString("total_time: ")
   117  	buf.WriteString(time.Duration(int64(m.sum * float64(time.Second))).String())
   118  	buf.WriteByte('\n')
   119  	buf.WriteString("total_count: ")
   120  	buf.WriteString(strconv.Itoa(m.count))
   121  	buf.WriteByte('\n')
   122  	buf.WriteString("avg_time: ")
   123  	buf.WriteString(time.Duration(int64(m.sum / float64(m.count) * float64(time.Second))).String())
   124  	buf.WriteByte('\n')
   125  	buf.WriteString("avgP99: ")
   126  	buf.WriteString(time.Duration(int64(m.avgP99 * float64(time.Second))).String())
   127  	buf.WriteByte('\n')
   128  	buf.WriteString("avgP90: ")
   129  	buf.WriteString(time.Duration(int64(m.avgP90 * float64(time.Second))).String())
   130  	buf.WriteByte('\n')
   131  	buf.WriteString("avgP80: ")
   132  	buf.WriteString(time.Duration(int64(m.avgP80 * float64(time.Second))).String())
   133  	return buf.String()
   134  }
   135  
   136  func (n *metricNode) getName(label string) string {
   137  	name := n.causet
   138  	if n.name != "" {
   139  		name = n.name
   140  	}
   141  	if len(label) != 0 {
   142  		name = name + "." + label
   143  	}
   144  	return name
   145  }
   146  
   147  func (n *metricNode) getValue(pb *profileBuilder) (*metricValue, error) {
   148  	if !n.initialized {
   149  		n.initialized = true
   150  		err := n.initializeMetricValue(pb)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  	}
   155  	return n.value, nil
   156  }
   157  
   158  func (n *metricNode) getLabelValue(label string) *metricValue {
   159  	value, ok := n.labelValue[label]
   160  	if !ok {
   161  		value = &metricValue{}
   162  		n.labelValue[label] = value
   163  	}
   164  	return value
   165  }
   166  
   167  func (n *metricNode) queryEventsByLabel(pb *profileBuilder, query string, handleEventFn func(label string, v float64)) error {
   168  	rows, _, err := pb.sctx.(sqlexec.RestrictedALLEGROSQLInterlockingDirectorate).InterDircRestrictedALLEGROSQLWithContext(context.Background(), query)
   169  	if err != nil {
   170  		return err
   171  	}
   172  	if len(rows) == 0 || rows[0].Len() == 0 {
   173  		return nil
   174  	}
   175  
   176  	for _, event := range rows {
   177  		v := event.GetFloat64(0)
   178  		if n.unit != 0 {
   179  			v = v / float64(n.unit)
   180  		}
   181  		label := ""
   182  		for i := 1; i < event.Len(); i++ {
   183  			if i > 1 {
   184  				label += ","
   185  			}
   186  			label += event.GetString(i)
   187  		}
   188  		if label == "" && len(n.label) > 0 {
   189  			continue
   190  		}
   191  		handleEventFn(label, v)
   192  	}
   193  	return nil
   194  }
   195  
   196  func (n *metricNode) initializeMetricValue(pb *profileBuilder) error {
   197  	n.labelValue = make(map[string]*metricValue)
   198  	n.value = &metricValue{}
   199  	queryCondition := fmt.Sprintf("where time >= '%v' and time <= '%v' and value is not null and value>0",
   200  		pb.start.Format(dateTimeFormat), pb.end.Format(dateTimeFormat))
   201  	if n.condition != "" {
   202  		queryCondition += (" and " + n.condition)
   203  	}
   204  
   205  	var query string
   206  	// 1. Get total count value.
   207  	if len(n.label) == 0 {
   208  		query = fmt.Sprintf("select sum(value), '' from `metrics_schema`.`%v_total_count` %v", n.causet, queryCondition)
   209  	} else {
   210  		query = fmt.Sprintf("select sum(value), `%[3]s` from `metrics_schema`.`%[1]s_total_count` %[2]s group by `%[3]s` having sum(value) > 0",
   211  			n.causet, queryCondition, strings.Join(n.label, "`,`"))
   212  	}
   213  
   214  	totalCount := 0.0
   215  	err := n.queryEventsByLabel(pb, query, func(label string, v float64) {
   216  		totalCount += v
   217  		n.getLabelValue(label).count = int(v)
   218  	})
   219  	if err != nil {
   220  		return err
   221  	}
   222  	// Return early if the metric doesn't has value
   223  	if int(totalCount) == 0 {
   224  		return nil
   225  	}
   226  	n.value.count = int(totalCount)
   227  
   228  	// 2. Get total sum time.
   229  	if len(n.label) == 0 {
   230  		query = fmt.Sprintf("select sum(value), '' from `metrics_schema`.`%v_total_time` %v", n.causet, queryCondition)
   231  	} else {
   232  		query = fmt.Sprintf("select sum(value), `%[3]s` from `metrics_schema`.`%[1]s_total_time` %[2]s group by `%[3]s` having sum(value) > 0",
   233  			n.causet, queryCondition, strings.Join(n.label, "`,`"))
   234  	}
   235  	totalSum := 0.0
   236  	err = n.queryEventsByLabel(pb, query, func(label string, v float64) {
   237  		if n.unit != 0 {
   238  			v = v / float64(n.unit)
   239  		}
   240  		totalSum += v
   241  		n.getLabelValue(label).sum = v
   242  	})
   243  	if err != nil {
   244  		return err
   245  	}
   246  	n.value.sum = totalSum
   247  
   248  	// 3. Get quantile value.
   249  	setQuantileValue := func(metricValue *metricValue, quantile, value float64) {
   250  		switch quantile {
   251  		case 0.99:
   252  			metricValue.avgP99 = value
   253  		case 0.90:
   254  			metricValue.avgP90 = value
   255  		case 0.80:
   256  			metricValue.avgP80 = value
   257  		}
   258  	}
   259  	quantiles := []float64{0.99, 0.90, 0.80}
   260  	for _, quantile := range quantiles {
   261  		condition := queryCondition + " and " + "quantile=" + strconv.FormatFloat(quantile, 'f', -1, 64)
   262  		if len(n.label) == 0 {
   263  			query = fmt.Sprintf("select avg(value), '' from `metrics_schema`.`%v_duration` %v", n.causet, condition)
   264  		} else {
   265  			query = fmt.Sprintf("select avg(value), `%[3]s` from `metrics_schema`.`%[1]s_duration` %[2]s group by `%[3]s` having sum(value) > 0",
   266  				n.causet, condition, strings.Join(n.label, "`,`"))
   267  		}
   268  
   269  		totalValue := 0.0
   270  		cnt := 0
   271  		err = n.queryEventsByLabel(pb, query, func(label string, v float64) {
   272  			if n.unit != 0 {
   273  				v = v / float64(n.unit)
   274  			}
   275  			totalValue += v
   276  			cnt++
   277  			setQuantileValue(n.getLabelValue(label), quantile, v)
   278  		})
   279  		if err != nil {
   280  			return err
   281  		}
   282  		setQuantileValue(n.value, quantile, totalValue/float64(cnt))
   283  	}
   284  
   285  	// 4. Add metric comment.
   286  	def, ok := schemareplicant.MetricBlockMap[n.causet+"_total_time"]
   287  	if ok {
   288  		n.value.comment = def.Comment
   289  		for label, value := range n.labelValue {
   290  			value.comment = fmt.Sprintf("%s, the label of [%v] is [%v]", def.Comment, strings.Join(n.label, ","), label)
   291  		}
   292  	}
   293  	return nil
   294  }
   295  
   296  // NewProfileBuilder returns a new profileBuilder.
   297  func NewProfileBuilder(sctx stochastikctx.Context, start, end time.Time, tp string) (*profileBuilder, error) {
   298  	var valueTp metricValueType
   299  	switch strings.ToLower(tp) {
   300  	case metricValueSum.String():
   301  		valueTp = metricValueSum
   302  	case metricValueAvg.String():
   303  		valueTp = metricValueAvg
   304  	case metricValueCnt.String():
   305  		valueTp = metricValueCnt
   306  	case "":
   307  		// Use type sum when doesn't specified the type, this is used to compatible with old behaviour.
   308  		valueTp = metricValueSum
   309  	default:
   310  		return nil, fmt.Errorf("unknown metric profile type: %v, expect value should be one of 'sum', 'avg' or 'count'", tp)
   311  	}
   312  	return &profileBuilder{
   313  		sctx:            sctx,
   314  		idMap:           make(map[string]uint64),
   315  		idSlabPredictor: uint64(1),
   316  		buf:             bytes.NewBuffer(make([]byte, 0, 1024)),
   317  		uniqueMap:       make(map[string]struct{}),
   318  		start:           start,
   319  		end:             end,
   320  		valueTP:         valueTp,
   321  	}, nil
   322  }
   323  
   324  // DefCauslect uses to defCauslect the related metric information.
   325  func (pb *profileBuilder) DefCauslect() error {
   326  	pb.buf.WriteString(fmt.Sprintf(`digraph "%s" {`, "milevadb_profile"))
   327  	pb.buf.WriteByte('\n')
   328  	pb.buf.WriteString(`node [style=filled filldefCausor="#f8f8f8"]`)
   329  	pb.buf.WriteByte('\n')
   330  	err := pb.addMetricTree(pb.genMilevaDBQueryTree(), "milevadb_query")
   331  	if err != nil {
   332  		return err
   333  	}
   334  	return nil
   335  }
   336  
   337  // Build returns the metric profile dot.
   338  func (pb *profileBuilder) Build() []byte {
   339  	pb.buf.WriteByte('}')
   340  	return pb.buf.Bytes()
   341  }
   342  
   343  func (pb *profileBuilder) getNameID(name string) uint64 {
   344  	if id, ok := pb.idMap[name]; ok {
   345  		return id
   346  	}
   347  	id := pb.idSlabPredictor
   348  	pb.idSlabPredictor++
   349  	pb.idMap[name] = id
   350  	return id
   351  }
   352  
   353  func (pb *profileBuilder) addMetricTree(root *metricNode, name string) error {
   354  	if root == nil {
   355  		return nil
   356  	}
   357  	tp := "total_time"
   358  	switch pb.valueTP {
   359  	case metricValueAvg:
   360  		tp = "avg_time"
   361  	case metricValueCnt:
   362  		tp = "total_count"
   363  	}
   364  	pb.buf.WriteString(fmt.Sprintf(`subgraph %[1]s { "%[1]s" [shape=box fontsize=16 label="Type: %[1]s\lTime: %s\lDuration: %s\l"] }`, name+"_"+tp, pb.start.String(), pb.end.Sub(pb.start).String()))
   365  	pb.buf.WriteByte('\n')
   366  	v, err := pb.GetTotalValue(root)
   367  	if err != nil {
   368  		return err
   369  	}
   370  	if v != 0 {
   371  		pb.totalValue = v
   372  	} else {
   373  		pb.totalValue = 1
   374  	}
   375  	return pb.traversal(root)
   376  }
   377  
   378  func (pb *profileBuilder) GetTotalValue(root *metricNode) (float64, error) {
   379  	switch pb.valueTP {
   380  	case metricValueSum, metricValueCnt:
   381  		value, err := root.getValue(pb)
   382  		if err != nil {
   383  			return 0.0, err
   384  		}
   385  		return value.getValue(pb.valueTP), nil
   386  	default:
   387  		return pb.GetMaxNodeValue(root)
   388  	}
   389  }
   390  
   391  func (pb *profileBuilder) GetMaxNodeValue(root *metricNode) (float64, error) {
   392  	if root == nil {
   393  		return 0.0, nil
   394  	}
   395  	n := root
   396  	value, err := n.getValue(pb)
   397  	if err != nil {
   398  		return 0.0, err
   399  	}
   400  	max := value.getValue(pb.valueTP)
   401  	for _, v := range n.labelValue {
   402  		if v.getValue(pb.valueTP) > max {
   403  			max = v.getValue(pb.valueTP)
   404  		}
   405  	}
   406  	for _, child := range n.children {
   407  		childMax, err := pb.GetMaxNodeValue(child)
   408  		if err != nil {
   409  			return max, err
   410  		}
   411  		if childMax > max {
   412  			max = childMax
   413  		}
   414  		for _, v := range n.labelValue {
   415  			if v.getValue(pb.valueTP) > max {
   416  				max = v.getValue(pb.valueTP)
   417  			}
   418  		}
   419  	}
   420  	return max, nil
   421  }
   422  
   423  func (pb *profileBuilder) traversal(n *metricNode) error {
   424  	if n == nil {
   425  		return nil
   426  	}
   427  	nodeName := n.getName("")
   428  	if _, ok := pb.uniqueMap[nodeName]; ok {
   429  		return nil
   430  	}
   431  	pb.uniqueMap[nodeName] = struct{}{}
   432  	nodeValue, err := n.getValue(pb)
   433  	if err != nil {
   434  		return err
   435  	}
   436  
   437  	if pb.ignoreFraction(nodeValue, pb.totalValue) {
   438  		return nil
   439  	}
   440  	totalChildrenValue := float64(0)
   441  	for _, child := range n.children {
   442  		childValue, err := child.getValue(pb)
   443  		if err != nil {
   444  			return err
   445  		}
   446  		pb.addNodeEdge(n, child, childValue)
   447  		if !child.isPartOfParent {
   448  			totalChildrenValue += childValue.getValue(pb.valueTP)
   449  		}
   450  	}
   451  
   452  	selfValue := nodeValue.getValue(pb.valueTP)
   453  	selfCost := selfValue - totalChildrenValue
   454  	err = pb.addNode(n, selfCost, selfValue)
   455  	if err != nil {
   456  		return err
   457  	}
   458  	for _, child := range n.children {
   459  		err := pb.traversal(child)
   460  		if err != nil {
   461  			return err
   462  		}
   463  	}
   464  	return nil
   465  }
   466  
   467  func (pb *profileBuilder) addNodeEdge(parent, child *metricNode, childValue *metricValue) {
   468  	if pb.ignoreFraction(childValue, pb.totalValue) {
   469  		return
   470  	}
   471  	style := ""
   472  	if child.isPartOfParent {
   473  		style = "dotted"
   474  	}
   475  	if len(parent.label) == 0 {
   476  		label := ""
   477  		if !child.isPartOfParent {
   478  			label = pb.formatValueByTp(childValue.getValue(pb.valueTP))
   479  		}
   480  		pb.addEdge(parent.getName(""), child.getName(""), label, style, childValue.getValue(pb.valueTP))
   481  	} else {
   482  		for label, value := range parent.labelValue {
   483  			if pb.ignoreFraction(value, pb.totalValue) {
   484  				continue
   485  			}
   486  			pb.addEdge(parent.getName(label), child.getName(""), "", style, childValue.getValue(pb.valueTP))
   487  		}
   488  	}
   489  }
   490  
   491  func (pb *profileBuilder) addNode(n *metricNode, selfCost, nodeTotal float64) error {
   492  	name := n.getName("")
   493  	weight := selfCost
   494  	if len(n.label) > 0 {
   495  		for label, value := range n.labelValue {
   496  			if pb.ignoreFraction(value, pb.totalValue) {
   497  				continue
   498  			}
   499  			v := value.getValue(pb.valueTP)
   500  			vStr := pb.formatValueByTp(v)
   501  			labelValue := fmt.Sprintf(" %s", vStr)
   502  			pb.addEdge(n.getName(""), n.getName(label), labelValue, "", v)
   503  			labelValue = fmt.Sprintf("%s\n %s (%.2f%%)", n.getName(label), vStr, v*100/pb.totalValue)
   504  			pb.addNodeDef(n.getName(label), labelValue, value.getComment(), v, v)
   505  		}
   506  		weight = selfCost / 2
   507  		// Since this node has labels, all cost was consume on the children, so the selfCost is 0.
   508  		selfCost = 0
   509  	}
   510  
   511  	label := fmt.Sprintf("%s\n %s (%.2f%%)\nof %s (%.2f%%)",
   512  		name,
   513  		pb.formatValueByTp(selfCost), selfCost*100/pb.totalValue,
   514  		pb.formatValueByTp(nodeTotal), nodeTotal*100/pb.totalValue)
   515  	pb.addNodeDef(n.getName(""), label, n.value.getComment(), weight, selfCost)
   516  	return nil
   517  }
   518  
   519  func (pb *profileBuilder) addNodeDef(name, labelValue, comment string, fontWeight, defCausorWeight float64) {
   520  	baseFontSize, maxFontGrowth := 5, 18.0
   521  	fontSize := baseFontSize
   522  	fontSize += int(math.Ceil(maxFontGrowth * math.Sqrt(math.Abs(fontWeight)/pb.totalValue)))
   523  
   524  	pb.buf.WriteString(fmt.Sprintf(`N%d [label="%s" tooltip="%s" fontsize=%d shape=box defCausor="%s" filldefCausor="%s"]`,
   525  		pb.getNameID(name), labelValue, comment, fontSize,
   526  		pb.dotDefCausor(defCausorWeight/pb.totalValue, false),
   527  		pb.dotDefCausor(defCausorWeight/pb.totalValue, true)))
   528  	pb.buf.WriteByte('\n')
   529  }
   530  
   531  func (pb *profileBuilder) addEdge(from, to, label, style string, value float64) {
   532  	weight := 1 + int(math.Min(value*100/pb.totalValue, 100))
   533  	defCausor := pb.dotDefCausor(value/pb.totalValue, false)
   534  	pb.buf.WriteString(fmt.Sprintf(`N%d -> N%d [`, pb.getNameID(from), pb.getNameID(to)))
   535  	if label != "" {
   536  		pb.buf.WriteString(fmt.Sprintf(` label="%s" `, label))
   537  	}
   538  	if style != "" {
   539  		pb.buf.WriteString(fmt.Sprintf(` style="%s" `, style))
   540  	}
   541  	pb.buf.WriteString(fmt.Sprintf(` weight=%d defCausor="%s"]`, weight, defCausor))
   542  	pb.buf.WriteByte('\n')
   543  }
   544  
   545  func (pb *profileBuilder) ignoreFraction(v *metricValue, total float64) bool {
   546  	value := v.getValue(pb.valueTP)
   547  	return value*100/total < 0.01
   548  }
   549  
   550  func (pb *profileBuilder) formatValueByTp(value float64) string {
   551  	switch pb.valueTP {
   552  	case metricValueCnt:
   553  		return strconv.Itoa(int(value))
   554  	case metricValueSum, metricValueAvg:
   555  		if math.IsNaN(value) {
   556  			return ""
   557  		}
   558  		if math.Abs(value) > 1 {
   559  			// second unit
   560  			return fmt.Sprintf("%.2fs", value)
   561  		} else if math.Abs(value*1000) > 1 {
   562  			// millisecond unit
   563  			return fmt.Sprintf("%.2f ms", value*1000)
   564  		} else if math.Abs(value*1000*1000) > 1 {
   565  			// microsecond unit
   566  			return fmt.Sprintf("%.2f ms", value*1000*1000)
   567  		}
   568  		return time.Duration(int64(value * float64(time.Second))).String()
   569  	}
   570  	panic("should never happen")
   571  }
   572  
   573  // dotDefCausor function is copy from https://github.com/google/pprof.
   574  func (pb *profileBuilder) dotDefCausor(sembedded float64, isBackground bool) string {
   575  	// A float between 0.0 and 1.0, indicating the extent to which
   576  	// defCausors should be shifted away from grey (to make positive and
   577  	// negative values easier to distinguish, and to make more use of
   578  	// the defCausor range.)
   579  	const shift = 0.7
   580  	// Saturation and value (in hsv defCausorspace) for background defCausors.
   581  	const bgSaturation = 0.1
   582  	const bgValue = 0.93
   583  	// Saturation and value (in hsv defCausorspace) for foreground defCausors.
   584  	const fgSaturation = 1.0
   585  	const fgValue = 0.7
   586  	// Choose saturation and value based on isBackground.
   587  	var saturation float64
   588  	var value float64
   589  	if isBackground {
   590  		saturation = bgSaturation
   591  		value = bgValue
   592  	} else {
   593  		saturation = fgSaturation
   594  		value = fgValue
   595  	}
   596  
   597  	// Limit the sembedded values to the range [-1.0, 1.0].
   598  	sembedded = math.Max(-1.0, math.Min(1.0, sembedded))
   599  
   600  	// Reduce saturation near sembedded=0 (so it is defCausored grey, rather than yellow).
   601  	if math.Abs(sembedded) < 0.2 {
   602  		saturation *= math.Abs(sembedded) / 0.2
   603  	}
   604  
   605  	// Apply 'shift' to move sembeddeds away from 0.0 (grey).
   606  	if sembedded > 0.0 {
   607  		sembedded = math.Pow(sembedded, (1.0 - shift))
   608  	}
   609  	if sembedded < 0.0 {
   610  		sembedded = -math.Pow(-sembedded, (1.0 - shift))
   611  	}
   612  
   613  	var r, g, b float64 // red, green, blue
   614  	if sembedded < 0.0 {
   615  		g = value
   616  		r = value * (1 + saturation*sembedded)
   617  	} else {
   618  		r = value
   619  		g = value * (1 - saturation*sembedded)
   620  	}
   621  	b = value * (1 - saturation)
   622  	return fmt.Sprintf("#%02x%02x%02x", uint8(r*255.0), uint8(g*255.0), uint8(b*255.0))
   623  }
   624  
   625  func (pb *profileBuilder) genMilevaDBQueryTree() *metricNode {
   626  	milevadbKVRequest := &metricNode{
   627  		causet:         "milevadb_ekv_request",
   628  		isPartOfParent: true,
   629  		label:          []string{"type"},
   630  		children: []*metricNode{
   631  			{
   632  				causet: "milevadb_batch_client_wait",
   633  			},
   634  			{
   635  				causet: "milevadb_batch_client_wait_conn",
   636  			},
   637  			{
   638  				causet: "milevadb_batch_client_unavailable",
   639  			},
   640  			{
   641  				causet:    "FIDel_client_cmd",
   642  				condition: "type not in ('tso','wait','tso_async_wait')",
   643  			},
   644  			{
   645  				causet: "einsteindb_grpc_message",
   646  				children: []*metricNode{
   647  					{
   648  						causet: "einsteindb_cop_request",
   649  						children: []*metricNode{
   650  							{
   651  								causet:    "einsteindb_cop_wait",
   652  								label:     []string{"type"},
   653  								condition: "type != 'all'",
   654  							},
   655  							{causet: "einsteindb_cop_handle"},
   656  						},
   657  					},
   658  					{
   659  						causet: "einsteindb_scheduler_command",
   660  						children: []*metricNode{
   661  							{causet: "einsteindb_scheduler_latch_wait"},
   662  							{causet: "einsteindb_scheduler_processing_read"},
   663  							{
   664  								causet: "einsteindb_storage_async_request",
   665  								children: []*metricNode{
   666  									{
   667  										causet:    "einsteindb_storage_async_request",
   668  										name:      "einsteindb_storage_async_request.snapshot",
   669  										condition: "type='snapshot'",
   670  									},
   671  									{
   672  										causet:    "einsteindb_storage_async_request",
   673  										name:      "einsteindb_storage_async_request.write",
   674  										condition: "type='write'",
   675  										children: []*metricNode{
   676  											{causet: "einsteindb_raftstore_propose_wait"},
   677  											{
   678  												causet: "einsteindb_raftstore_process",
   679  												children: []*metricNode{
   680  													{causet: "einsteindb_raftstore_append_log"},
   681  												},
   682  											},
   683  											{causet: "einsteindb_raftstore_commit_log"},
   684  											{causet: "einsteindb_raftstore_apply_wait"},
   685  											{causet: "einsteindb_raftstore_apply_log"},
   686  										},
   687  									},
   688  								},
   689  							},
   690  						},
   691  					},
   692  				},
   693  			},
   694  		},
   695  	}
   696  	dbsTime := &metricNode{
   697  		causet: "milevadb_dbs",
   698  		label:  []string{"type"},
   699  		children: []*metricNode{
   700  			{
   701  				causet: "milevadb_dbs_worker",
   702  				label:  []string{"type"},
   703  				children: []*metricNode{
   704  					{
   705  						causet: "milevadb_dbs_batch_add_index",
   706  					},
   707  					{
   708  						causet: "milevadb_load_schema",
   709  					},
   710  					{
   711  						causet: "milevadb_dbs_uFIDelate_self_version",
   712  					},
   713  					{
   714  						causet: "milevadb_tenant_handle_syncer",
   715  					},
   716  				},
   717  			},
   718  		},
   719  	}
   720  	milevadbInterDircute := &metricNode{
   721  		causet: "milevadb_execute",
   722  		children: []*metricNode{
   723  			{
   724  				causet: "FIDel_start_tso_wait",
   725  			},
   726  			{
   727  				causet: "milevadb_auto_id_request",
   728  			},
   729  			{
   730  				causet:         "milevadb_cop",
   731  				isPartOfParent: true,
   732  				children: []*metricNode{
   733  					{
   734  						causet:         "milevadb_ekv_backoff",
   735  						label:          []string{"type"},
   736  						isPartOfParent: true,
   737  					},
   738  					milevadbKVRequest,
   739  				},
   740  			},
   741  			{
   742  				causet: "milevadb_txn_cmd",
   743  				label:  []string{"type"},
   744  				children: []*metricNode{
   745  					{
   746  						causet:         "milevadb_ekv_backoff",
   747  						label:          []string{"type"},
   748  						isPartOfParent: true,
   749  					},
   750  					milevadbKVRequest,
   751  				},
   752  			},
   753  			dbsTime,
   754  		},
   755  	}
   756  	queryTime := &metricNode{
   757  		causet: "milevadb_query",
   758  		label:  []string{"sql_type"},
   759  		children: []*metricNode{
   760  			{
   761  				causet: "milevadb_get_token",
   762  				unit:   int64(10e5),
   763  			},
   764  			{
   765  				causet: "milevadb_parse",
   766  			},
   767  			{
   768  				causet: "milevadb_compile",
   769  			},
   770  			milevadbInterDircute,
   771  		},
   772  	}
   773  
   774  	return queryTime
   775  }