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 }