github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/soliton/stmtsummary/statement_summary.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 stmtsummary 15 16 import ( 17 "bytes" 18 "container/list" 19 "fmt" 20 "sort" 21 "strings" 22 "sync" 23 "sync/atomic" 24 "time" 25 26 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 27 "github.com/whtcorpsinc/BerolinaSQL/auth" 28 "github.com/whtcorpsinc/milevadb/soliton/ekvcache" 29 "github.com/whtcorpsinc/milevadb/soliton/execdetails" 30 "github.com/whtcorpsinc/milevadb/soliton/logutil" 31 "github.com/whtcorpsinc/milevadb/soliton/plancodec" 32 "github.com/whtcorpsinc/milevadb/soliton/replog" 33 "github.com/whtcorpsinc/milevadb/stochastikctx/stmtctx" 34 "github.com/whtcorpsinc/milevadb/types" 35 "go.uber.org/zap" 36 ) 37 38 // stmtSummaryByDigestKey defines key for stmtSummaryByDigestMap.summaryMap. 39 type stmtSummaryByDigestKey struct { 40 // Same memexs may appear in different schemaReplicant, but they refer to different blocks. 41 schemaName string 42 digest string 43 // The digest of the previous memex. 44 prevDigest string 45 // The digest of the plan of this ALLEGROALLEGROSQL. 46 planDigest string 47 // `hash` is the hash value of this object. 48 hash []byte 49 } 50 51 // Hash implements SimpleLRUCache.Key. 52 // Only when current ALLEGROALLEGROSQL is `commit` do we record `prevALLEGROSQL`. Otherwise, `prevALLEGROSQL` is empty. 53 // `prevALLEGROSQL` is included in the key To distinguish different transactions. 54 func (key *stmtSummaryByDigestKey) Hash() []byte { 55 if len(key.hash) == 0 { 56 key.hash = make([]byte, 0, len(key.schemaName)+len(key.digest)+len(key.prevDigest)+len(key.planDigest)) 57 key.hash = append(key.hash, replog.Slice(key.digest)...) 58 key.hash = append(key.hash, replog.Slice(key.schemaName)...) 59 key.hash = append(key.hash, replog.Slice(key.prevDigest)...) 60 key.hash = append(key.hash, replog.Slice(key.planDigest)...) 61 } 62 return key.hash 63 } 64 65 // stmtSummaryByDigestMap is a LRU cache that stores memex summaries. 66 type stmtSummaryByDigestMap struct { 67 // It's rare to read concurrently, so RWMutex is not needed. 68 sync.Mutex 69 summaryMap *ekvcache.SimpleLRUCache 70 // beginTimeForCurInterval is the begin time for current summary. 71 beginTimeForCurInterval int64 72 73 // sysVars encapsulates system variables needed to control memex summary. 74 sysVars *systemVars 75 } 76 77 // StmtSummaryByDigestMap is a global map containing all memex summaries. 78 var StmtSummaryByDigestMap = newStmtSummaryByDigestMap() 79 80 // stmtSummaryByDigest is the summary for each type of memexs. 81 type stmtSummaryByDigest struct { 82 // It's rare to read concurrently, so RWMutex is not needed. 83 // Mutex is only used to dagger `history`. 84 sync.Mutex 85 initialized bool 86 // Each element in history is a summary in one interval. 87 history *list.List 88 // Following fields are common for each summary element. 89 // They won't change once this object is created, so locking is not needed. 90 schemaName string 91 digest string 92 planDigest string 93 stmtType string 94 normalizedALLEGROSQL string 95 blockNames string 96 isInternal bool 97 } 98 99 // stmtSummaryByDigestElement is the summary for each type of memexs in current interval. 100 type stmtSummaryByDigestElement struct { 101 sync.Mutex 102 // Each summary is summarized between [beginTime, endTime). 103 beginTime int64 104 endTime int64 105 // basic 106 sampleALLEGROSQL string 107 prevALLEGROSQL string 108 sampleCauset string 109 indexNames []string 110 execCount int64 111 sumErrors int 112 sumWarnings int 113 // latency 114 sumLatency time.Duration 115 maxLatency time.Duration 116 minLatency time.Duration 117 sumParseLatency time.Duration 118 maxParseLatency time.Duration 119 sumCompileLatency time.Duration 120 maxCompileLatency time.Duration 121 // interlock 122 sumNumCausetTasks int64 123 maxCopProcessTime time.Duration 124 maxCopProcessAddress string 125 maxCopWaitTime time.Duration 126 maxCopWaitAddress string 127 // EinsteinDB 128 sumProcessTime time.Duration 129 maxProcessTime time.Duration 130 sumWaitTime time.Duration 131 maxWaitTime time.Duration 132 sumBackoffTime time.Duration 133 maxBackoffTime time.Duration 134 sumTotalKeys int64 135 maxTotalKeys int64 136 sumProcessedKeys int64 137 maxProcessedKeys int64 138 // txn 139 commitCount int64 140 sumGetCommitTsTime time.Duration 141 maxGetCommitTsTime time.Duration 142 sumPrewriteTime time.Duration 143 maxPrewriteTime time.Duration 144 sumCommitTime time.Duration 145 maxCommitTime time.Duration 146 sumLocalLatchTime time.Duration 147 maxLocalLatchTime time.Duration 148 sumCommitBackoffTime int64 149 maxCommitBackoffTime int64 150 sumResolveLockTime int64 151 maxResolveLockTime int64 152 sumWriteKeys int64 153 maxWriteKeys int 154 sumWriteSize int64 155 maxWriteSize int 156 sumPrewriteRegionNum int64 157 maxPrewriteRegionNum int32 158 sumTxnRetry int64 159 maxTxnRetry int 160 sumInterDircRetryCount int64 161 sumInterDircRetryTime time.Duration 162 sumBackoffTimes int64 163 backoffTypes map[fmt.Stringer]int 164 authUsers map[string]struct{} 165 // other 166 sumMem int64 167 maxMem int64 168 sumDisk int64 169 maxDisk int64 170 sumAffectedRows uint64 171 // The first time this type of ALLEGROALLEGROSQL executes. 172 firstSeen time.Time 173 // The last time this type of ALLEGROALLEGROSQL executes. 174 lastSeen time.Time 175 // plan cache 176 planInCache bool 177 planCacheHits int64 178 // pessimistic execution retry information. 179 execRetryCount uint 180 execRetryTime time.Duration 181 } 182 183 // StmtInterDircInfo records execution information of each memex. 184 type StmtInterDircInfo struct { 185 SchemaName string 186 OriginalALLEGROSQL string 187 NormalizedALLEGROSQL string 188 Digest string 189 PrevALLEGROSQL string 190 PrevALLEGROSQLDigest string 191 CausetGenerator func() string 192 CausetDigest string 193 CausetDigestGen func() string 194 User string 195 TotalLatency time.Duration 196 ParseLatency time.Duration 197 CompileLatency time.Duration 198 StmtCtx *stmtctx.StatementContext 199 CausetTasks *stmtctx.CausetTasksDetails 200 InterDircDetail *execdetails.InterDircDetails 201 MemMax int64 202 DiskMax int64 203 StartTime time.Time 204 IsInternal bool 205 Succeed bool 206 CausetInCache bool 207 InterDircRetryCount uint 208 InterDircRetryTime time.Duration 209 } 210 211 // newStmtSummaryByDigestMap creates an empty stmtSummaryByDigestMap. 212 func newStmtSummaryByDigestMap() *stmtSummaryByDigestMap { 213 sysVars := newSysVars() 214 maxStmtCount := uint(sysVars.getVariable(typeMaxStmtCount)) 215 return &stmtSummaryByDigestMap{ 216 summaryMap: ekvcache.NewSimpleLRUCache(maxStmtCount, 0, 0), 217 sysVars: sysVars, 218 } 219 } 220 221 // AddStatement adds a memex to StmtSummaryByDigestMap. 222 func (ssMap *stmtSummaryByDigestMap) AddStatement(sei *StmtInterDircInfo) { 223 // All times are counted in seconds. 224 now := time.Now().Unix() 225 226 intervalSeconds := ssMap.refreshInterval() 227 historySize := ssMap.historySize() 228 229 key := &stmtSummaryByDigestKey{ 230 schemaName: sei.SchemaName, 231 digest: sei.Digest, 232 prevDigest: sei.PrevALLEGROSQLDigest, 233 planDigest: sei.CausetDigest, 234 } 235 // Calculate hash value in advance, to reduce the time holding the dagger. 236 key.Hash() 237 238 // Enclose the causet in a function to ensure the dagger will always be released. 239 summary, beginTime := func() (*stmtSummaryByDigest, int64) { 240 ssMap.Lock() 241 defer ssMap.Unlock() 242 243 // Check again. Statements could be added before disabling the flag and after Clear(). 244 if !ssMap.Enabled() { 245 return nil, 0 246 } 247 if sei.IsInternal && !ssMap.EnabledInternal() { 248 return nil, 0 249 } 250 251 if ssMap.beginTimeForCurInterval+intervalSeconds <= now { 252 // `beginTimeForCurInterval` is a multiple of intervalSeconds, so that when the interval is a multiple 253 // of 60 (or 600, 1800, 3600, etc), begin time shows 'XX:XX:00', not 'XX:XX:01'~'XX:XX:59'. 254 ssMap.beginTimeForCurInterval = now / intervalSeconds * intervalSeconds 255 } 256 257 beginTime := ssMap.beginTimeForCurInterval 258 value, ok := ssMap.summaryMap.Get(key) 259 var summary *stmtSummaryByDigest 260 if !ok { 261 // Lazy initialize it to release ssMap.mutex ASAP. 262 summary = new(stmtSummaryByDigest) 263 ssMap.summaryMap.Put(key, summary) 264 } else { 265 summary = value.(*stmtSummaryByDigest) 266 } 267 summary.isInternal = summary.isInternal && sei.IsInternal 268 return summary, beginTime 269 }() 270 271 // Lock a single entry, not the whole cache. 272 if summary != nil { 273 summary.add(sei, beginTime, intervalSeconds, historySize) 274 } 275 } 276 277 // Clear removes all memex summaries. 278 func (ssMap *stmtSummaryByDigestMap) Clear() { 279 ssMap.Lock() 280 defer ssMap.Unlock() 281 282 ssMap.summaryMap.DeleteAll() 283 ssMap.beginTimeForCurInterval = 0 284 } 285 286 // clearInternal removes all memex summaries which are internal summaries. 287 func (ssMap *stmtSummaryByDigestMap) clearInternal() { 288 ssMap.Lock() 289 defer ssMap.Unlock() 290 291 for _, key := range ssMap.summaryMap.Keys() { 292 summary, ok := ssMap.summaryMap.Get(key) 293 if !ok { 294 continue 295 } 296 if summary.(*stmtSummaryByDigest).isInternal { 297 ssMap.summaryMap.Delete(key) 298 } 299 } 300 } 301 302 // ToCurrentCauset converts current memex summaries to causet. 303 func (ssMap *stmtSummaryByDigestMap) ToCurrentCauset(user *auth.UserIdentity, isSuper bool) [][]types.Causet { 304 ssMap.Lock() 305 values := ssMap.summaryMap.Values() 306 beginTime := ssMap.beginTimeForCurInterval 307 ssMap.Unlock() 308 309 rows := make([][]types.Causet, 0, len(values)) 310 for _, value := range values { 311 record := value.(*stmtSummaryByDigest).toCurrentCauset(beginTime, user, isSuper) 312 if record != nil { 313 rows = append(rows, record) 314 } 315 } 316 return rows 317 } 318 319 // ToHistoryCauset converts history memexs summaries to causet. 320 func (ssMap *stmtSummaryByDigestMap) ToHistoryCauset(user *auth.UserIdentity, isSuper bool) [][]types.Causet { 321 ssMap.Lock() 322 values := ssMap.summaryMap.Values() 323 ssMap.Unlock() 324 325 historySize := ssMap.historySize() 326 rows := make([][]types.Causet, 0, len(values)*historySize) 327 for _, value := range values { 328 records := value.(*stmtSummaryByDigest).toHistoryCauset(historySize, user, isSuper) 329 rows = append(rows, records...) 330 } 331 return rows 332 } 333 334 // GetMoreThanOnceSelect gets users' select ALLEGROSQLs that occurred more than once. 335 func (ssMap *stmtSummaryByDigestMap) GetMoreThanOnceSelect() ([]string, []string) { 336 ssMap.Lock() 337 values := ssMap.summaryMap.Values() 338 ssMap.Unlock() 339 340 schemas := make([]string, 0, len(values)) 341 sqls := make([]string, 0, len(values)) 342 for _, value := range values { 343 ssbd := value.(*stmtSummaryByDigest) 344 func() { 345 ssbd.Lock() 346 defer ssbd.Unlock() 347 if ssbd.initialized && ssbd.stmtType == "Select" { 348 if ssbd.history.Len() > 0 { 349 ssElement := ssbd.history.Back().Value.(*stmtSummaryByDigestElement) 350 ssElement.Lock() 351 352 // Empty auth users means that it is an internal queries. 353 if len(ssElement.authUsers) > 0 && (ssbd.history.Len() > 1 || ssElement.execCount > 1) { 354 schemas = append(schemas, ssbd.schemaName) 355 sqls = append(sqls, ssElement.sampleALLEGROSQL) 356 } 357 ssElement.Unlock() 358 } 359 } 360 }() 361 } 362 return schemas, sqls 363 } 364 365 // SetEnabled enables or disables memex summary in global(cluster) or stochastik(server) scope. 366 func (ssMap *stmtSummaryByDigestMap) SetEnabled(value string, inStochastik bool) error { 367 if err := ssMap.sysVars.setVariable(typeEnable, value, inStochastik); err != nil { 368 return err 369 } 370 371 // Clear all summaries once memex summary is disabled. 372 if ssMap.sysVars.getVariable(typeEnable) == 0 { 373 ssMap.Clear() 374 } 375 return nil 376 } 377 378 // Enabled returns whether memex summary is enabled. 379 func (ssMap *stmtSummaryByDigestMap) Enabled() bool { 380 return ssMap.sysVars.getVariable(typeEnable) > 0 381 } 382 383 // SetEnabledInternalQuery enables or disables internal memex summary in global(cluster) or stochastik(server) scope. 384 func (ssMap *stmtSummaryByDigestMap) SetEnabledInternalQuery(value string, inStochastik bool) error { 385 if err := ssMap.sysVars.setVariable(typeEnableInternalQuery, value, inStochastik); err != nil { 386 return err 387 } 388 389 // Clear all summaries once memex summary is disabled. 390 if ssMap.sysVars.getVariable(typeEnableInternalQuery) == 0 { 391 ssMap.clearInternal() 392 } 393 return nil 394 } 395 396 // EnabledInternal returns whether internal memex summary is enabled. 397 func (ssMap *stmtSummaryByDigestMap) EnabledInternal() bool { 398 return ssMap.sysVars.getVariable(typeEnableInternalQuery) > 0 399 } 400 401 // SetRefreshInterval sets refreshing interval in ssMap.sysVars. 402 func (ssMap *stmtSummaryByDigestMap) SetRefreshInterval(value string, inStochastik bool) error { 403 return ssMap.sysVars.setVariable(typeRefreshInterval, value, inStochastik) 404 } 405 406 // refreshInterval gets the refresh interval for summaries. 407 func (ssMap *stmtSummaryByDigestMap) refreshInterval() int64 { 408 return ssMap.sysVars.getVariable(typeRefreshInterval) 409 } 410 411 // SetHistorySize sets the history size for all summaries. 412 func (ssMap *stmtSummaryByDigestMap) SetHistorySize(value string, inStochastik bool) error { 413 return ssMap.sysVars.setVariable(typeHistorySize, value, inStochastik) 414 } 415 416 // historySize gets the history size for summaries. 417 func (ssMap *stmtSummaryByDigestMap) historySize() int { 418 return int(ssMap.sysVars.getVariable(typeHistorySize)) 419 } 420 421 // SetHistorySize sets the history size for all summaries. 422 func (ssMap *stmtSummaryByDigestMap) SetMaxStmtCount(value string, inStochastik bool) error { 423 if err := ssMap.sysVars.setVariable(typeMaxStmtCount, value, inStochastik); err != nil { 424 return err 425 } 426 capacity := ssMap.sysVars.getVariable(typeMaxStmtCount) 427 428 ssMap.Lock() 429 defer ssMap.Unlock() 430 return ssMap.summaryMap.SetCapacity(uint(capacity)) 431 } 432 433 func (ssMap *stmtSummaryByDigestMap) maxStmtCount() int { 434 return int(ssMap.sysVars.getVariable(typeMaxStmtCount)) 435 } 436 437 // SetHistorySize sets the history size for all summaries. 438 func (ssMap *stmtSummaryByDigestMap) SetMaxALLEGROSQLLength(value string, inStochastik bool) error { 439 return ssMap.sysVars.setVariable(typeMaxALLEGROSQLLength, value, inStochastik) 440 } 441 442 func (ssMap *stmtSummaryByDigestMap) maxALLEGROSQLLength() int { 443 return int(ssMap.sysVars.getVariable(typeMaxALLEGROSQLLength)) 444 } 445 446 // newStmtSummaryByDigest creates a stmtSummaryByDigest from StmtInterDircInfo. 447 func (ssbd *stmtSummaryByDigest) init(sei *StmtInterDircInfo, beginTime int64, intervalSeconds int64, historySize int) { 448 // Use "," to separate causet names to support FIND_IN_SET. 449 var buffer bytes.Buffer 450 for i, value := range sei.StmtCtx.Blocks { 451 // In `create database` memex, EDB name is not empty but causet name is empty. 452 if len(value.Block) == 0 { 453 continue 454 } 455 buffer.WriteString(strings.ToLower(value.EDB)) 456 buffer.WriteString(".") 457 buffer.WriteString(strings.ToLower(value.Block)) 458 if i < len(sei.StmtCtx.Blocks)-1 { 459 buffer.WriteString(",") 460 } 461 } 462 blockNames := buffer.String() 463 464 planDigest := sei.CausetDigest 465 if sei.CausetDigestGen != nil && len(planDigest) == 0 { 466 // It comes here only when the plan is 'Point_Get'. 467 planDigest = sei.CausetDigestGen() 468 } 469 ssbd.schemaName = sei.SchemaName 470 ssbd.digest = sei.Digest 471 ssbd.planDigest = planDigest 472 ssbd.stmtType = sei.StmtCtx.StmtType 473 ssbd.normalizedALLEGROSQL = formatALLEGROSQL(sei.NormalizedALLEGROSQL) 474 ssbd.blockNames = blockNames 475 ssbd.history = list.New() 476 ssbd.initialized = true 477 } 478 479 func (ssbd *stmtSummaryByDigest) add(sei *StmtInterDircInfo, beginTime int64, intervalSeconds int64, historySize int) { 480 // Enclose this causet in a function to ensure the dagger will always be released. 481 ssElement, isElementNew := func() (*stmtSummaryByDigestElement, bool) { 482 ssbd.Lock() 483 defer ssbd.Unlock() 484 485 if !ssbd.initialized { 486 ssbd.init(sei, beginTime, intervalSeconds, historySize) 487 } 488 489 var ssElement *stmtSummaryByDigestElement 490 isElementNew := true 491 if ssbd.history.Len() > 0 { 492 lastElement := ssbd.history.Back().Value.(*stmtSummaryByDigestElement) 493 if lastElement.beginTime >= beginTime { 494 ssElement = lastElement 495 isElementNew = false 496 } else { 497 // The last elements expires to the history. 498 lastElement.onExpire(intervalSeconds) 499 } 500 } 501 if isElementNew { 502 // If the element is new created, `ssElement.add(sei)` should be done inside the dagger of `ssbd`. 503 ssElement = newStmtSummaryByDigestElement(sei, beginTime, intervalSeconds) 504 ssbd.history.PushBack(ssElement) 505 } 506 507 // `historySize` might be modified anytime, so check expiration every time. 508 // Even if history is set to 0, current summary is still needed. 509 for ssbd.history.Len() > historySize && ssbd.history.Len() > 1 { 510 ssbd.history.Remove(ssbd.history.Front()) 511 } 512 513 return ssElement, isElementNew 514 }() 515 516 // Lock a single entry, not the whole `ssbd`. 517 if !isElementNew { 518 ssElement.add(sei, intervalSeconds) 519 } 520 } 521 522 func (ssbd *stmtSummaryByDigest) toCurrentCauset(beginTimeForCurInterval int64, user *auth.UserIdentity, isSuper bool) []types.Causet { 523 var ssElement *stmtSummaryByDigestElement 524 525 ssbd.Lock() 526 if ssbd.initialized && ssbd.history.Len() > 0 { 527 ssElement = ssbd.history.Back().Value.(*stmtSummaryByDigestElement) 528 } 529 ssbd.Unlock() 530 531 // `ssElement` is lazy expired, so expired elements could also be read. 532 // `beginTime` won't change since `ssElement` is created, so locking is not needed here. 533 isAuthed := true 534 if user != nil && !isSuper { 535 _, isAuthed = ssElement.authUsers[user.Username] 536 } 537 if ssElement == nil || ssElement.beginTime < beginTimeForCurInterval || !isAuthed { 538 return nil 539 } 540 return ssElement.toCauset(ssbd) 541 } 542 543 func (ssbd *stmtSummaryByDigest) toHistoryCauset(historySize int, user *auth.UserIdentity, isSuper bool) [][]types.Causet { 544 // DefCauslect all history summaries to an array. 545 ssElements := ssbd.defCauslectHistorySummaries(historySize) 546 547 rows := make([][]types.Causet, 0, len(ssElements)) 548 for _, ssElement := range ssElements { 549 isAuthed := true 550 if user != nil && !isSuper { 551 _, isAuthed = ssElement.authUsers[user.Username] 552 } 553 if isAuthed { 554 rows = append(rows, ssElement.toCauset(ssbd)) 555 } 556 } 557 return rows 558 } 559 560 // defCauslectHistorySummaries puts at most `historySize` summaries to an array. 561 func (ssbd *stmtSummaryByDigest) defCauslectHistorySummaries(historySize int) []*stmtSummaryByDigestElement { 562 ssbd.Lock() 563 defer ssbd.Unlock() 564 565 if !ssbd.initialized { 566 return nil 567 } 568 ssElements := make([]*stmtSummaryByDigestElement, 0, ssbd.history.Len()) 569 for listElement := ssbd.history.Front(); listElement != nil && len(ssElements) < historySize; listElement = listElement.Next() { 570 ssElement := listElement.Value.(*stmtSummaryByDigestElement) 571 ssElements = append(ssElements, ssElement) 572 } 573 return ssElements 574 } 575 576 func newStmtSummaryByDigestElement(sei *StmtInterDircInfo, beginTime int64, intervalSeconds int64) *stmtSummaryByDigestElement { 577 // sampleALLEGROSQL / authUsers(sampleUser) / sampleCauset / prevALLEGROSQL / indexNames causetstore the values shown at the first time, 578 // because it compacts performance to uFIDelate every time. 579 ssElement := &stmtSummaryByDigestElement{ 580 beginTime: beginTime, 581 sampleALLEGROSQL: formatALLEGROSQL(sei.OriginalALLEGROSQL), 582 // PrevALLEGROSQL is already truncated to cfg.Log.QueryLogMaxLen. 583 prevALLEGROSQL: sei.PrevALLEGROSQL, 584 // sampleCauset needs to be decoded so it can't be truncated. 585 sampleCauset: sei.CausetGenerator(), 586 indexNames: sei.StmtCtx.IndexNames, 587 minLatency: sei.TotalLatency, 588 firstSeen: sei.StartTime, 589 lastSeen: sei.StartTime, 590 backoffTypes: make(map[fmt.Stringer]int), 591 authUsers: make(map[string]struct{}), 592 planInCache: false, 593 planCacheHits: 0, 594 } 595 ssElement.add(sei, intervalSeconds) 596 return ssElement 597 } 598 599 // onExpire is called when this element expires to history. 600 func (ssElement *stmtSummaryByDigestElement) onExpire(intervalSeconds int64) { 601 ssElement.Lock() 602 defer ssElement.Unlock() 603 604 // refreshInterval may change anytime, so we need to uFIDelate endTime. 605 if ssElement.beginTime+intervalSeconds > ssElement.endTime { 606 // // If interval changes to a bigger value, uFIDelate endTime to beginTime + interval. 607 ssElement.endTime = ssElement.beginTime + intervalSeconds 608 } else if ssElement.beginTime+intervalSeconds < ssElement.endTime { 609 now := time.Now().Unix() 610 // If interval changes to a smaller value and now > beginTime + interval, uFIDelate endTime to current time. 611 if now > ssElement.beginTime+intervalSeconds { 612 ssElement.endTime = now 613 } 614 } 615 } 616 617 func (ssElement *stmtSummaryByDigestElement) add(sei *StmtInterDircInfo, intervalSeconds int64) { 618 ssElement.Lock() 619 defer ssElement.Unlock() 620 621 // add user to auth users set 622 if len(sei.User) > 0 { 623 ssElement.authUsers[sei.User] = struct{}{} 624 } 625 626 // refreshInterval may change anytime, uFIDelate endTime ASAP. 627 ssElement.endTime = ssElement.beginTime + intervalSeconds 628 ssElement.execCount++ 629 if !sei.Succeed { 630 ssElement.sumErrors += 1 631 } 632 ssElement.sumWarnings += int(sei.StmtCtx.WarningCount()) 633 634 // latency 635 ssElement.sumLatency += sei.TotalLatency 636 if sei.TotalLatency > ssElement.maxLatency { 637 ssElement.maxLatency = sei.TotalLatency 638 } 639 if sei.TotalLatency < ssElement.minLatency { 640 ssElement.minLatency = sei.TotalLatency 641 } 642 ssElement.sumParseLatency += sei.ParseLatency 643 if sei.ParseLatency > ssElement.maxParseLatency { 644 ssElement.maxParseLatency = sei.ParseLatency 645 } 646 ssElement.sumCompileLatency += sei.CompileLatency 647 if sei.CompileLatency > ssElement.maxCompileLatency { 648 ssElement.maxCompileLatency = sei.CompileLatency 649 } 650 651 // interlock 652 numCausetTasks := int64(sei.CausetTasks.NumCausetTasks) 653 ssElement.sumNumCausetTasks += numCausetTasks 654 if sei.CausetTasks.MaxProcessTime > ssElement.maxCopProcessTime { 655 ssElement.maxCopProcessTime = sei.CausetTasks.MaxProcessTime 656 ssElement.maxCopProcessAddress = sei.CausetTasks.MaxProcessAddress 657 } 658 if sei.CausetTasks.MaxWaitTime > ssElement.maxCopWaitTime { 659 ssElement.maxCopWaitTime = sei.CausetTasks.MaxWaitTime 660 ssElement.maxCopWaitAddress = sei.CausetTasks.MaxWaitAddress 661 } 662 663 // EinsteinDB 664 ssElement.sumProcessTime += sei.InterDircDetail.ProcessTime 665 if sei.InterDircDetail.ProcessTime > ssElement.maxProcessTime { 666 ssElement.maxProcessTime = sei.InterDircDetail.ProcessTime 667 } 668 ssElement.sumWaitTime += sei.InterDircDetail.WaitTime 669 if sei.InterDircDetail.WaitTime > ssElement.maxWaitTime { 670 ssElement.maxWaitTime = sei.InterDircDetail.WaitTime 671 } 672 ssElement.sumBackoffTime += sei.InterDircDetail.BackoffTime 673 if sei.InterDircDetail.BackoffTime > ssElement.maxBackoffTime { 674 ssElement.maxBackoffTime = sei.InterDircDetail.BackoffTime 675 } 676 ssElement.sumTotalKeys += sei.InterDircDetail.TotalKeys 677 if sei.InterDircDetail.TotalKeys > ssElement.maxTotalKeys { 678 ssElement.maxTotalKeys = sei.InterDircDetail.TotalKeys 679 } 680 ssElement.sumProcessedKeys += sei.InterDircDetail.ProcessedKeys 681 if sei.InterDircDetail.ProcessedKeys > ssElement.maxProcessedKeys { 682 ssElement.maxProcessedKeys = sei.InterDircDetail.ProcessedKeys 683 } 684 685 // txn 686 commitDetails := sei.InterDircDetail.CommitDetail 687 if commitDetails != nil { 688 ssElement.commitCount++ 689 ssElement.sumPrewriteTime += commitDetails.PrewriteTime 690 if commitDetails.PrewriteTime > ssElement.maxPrewriteTime { 691 ssElement.maxPrewriteTime = commitDetails.PrewriteTime 692 } 693 ssElement.sumCommitTime += commitDetails.CommitTime 694 if commitDetails.CommitTime > ssElement.maxCommitTime { 695 ssElement.maxCommitTime = commitDetails.CommitTime 696 } 697 ssElement.sumGetCommitTsTime += commitDetails.GetCommitTsTime 698 if commitDetails.GetCommitTsTime > ssElement.maxGetCommitTsTime { 699 ssElement.maxGetCommitTsTime = commitDetails.GetCommitTsTime 700 } 701 commitBackoffTime := atomic.LoadInt64(&commitDetails.CommitBackoffTime) 702 ssElement.sumCommitBackoffTime += commitBackoffTime 703 if commitBackoffTime > ssElement.maxCommitBackoffTime { 704 ssElement.maxCommitBackoffTime = commitBackoffTime 705 } 706 resolveLockTime := atomic.LoadInt64(&commitDetails.ResolveLockTime) 707 ssElement.sumResolveLockTime += resolveLockTime 708 if resolveLockTime > ssElement.maxResolveLockTime { 709 ssElement.maxResolveLockTime = resolveLockTime 710 } 711 ssElement.sumLocalLatchTime += commitDetails.LocalLatchTime 712 if commitDetails.LocalLatchTime > ssElement.maxLocalLatchTime { 713 ssElement.maxLocalLatchTime = commitDetails.LocalLatchTime 714 } 715 ssElement.sumWriteKeys += int64(commitDetails.WriteKeys) 716 if commitDetails.WriteKeys > ssElement.maxWriteKeys { 717 ssElement.maxWriteKeys = commitDetails.WriteKeys 718 } 719 ssElement.sumWriteSize += int64(commitDetails.WriteSize) 720 if commitDetails.WriteSize > ssElement.maxWriteSize { 721 ssElement.maxWriteSize = commitDetails.WriteSize 722 } 723 prewriteRegionNum := atomic.LoadInt32(&commitDetails.PrewriteRegionNum) 724 ssElement.sumPrewriteRegionNum += int64(prewriteRegionNum) 725 if prewriteRegionNum > ssElement.maxPrewriteRegionNum { 726 ssElement.maxPrewriteRegionNum = prewriteRegionNum 727 } 728 ssElement.sumTxnRetry += int64(commitDetails.TxnRetry) 729 if commitDetails.TxnRetry > ssElement.maxTxnRetry { 730 ssElement.maxTxnRetry = commitDetails.TxnRetry 731 } 732 commitDetails.Mu.Lock() 733 ssElement.sumBackoffTimes += int64(len(commitDetails.Mu.BackoffTypes)) 734 for _, backoffType := range commitDetails.Mu.BackoffTypes { 735 ssElement.backoffTypes[backoffType] += 1 736 } 737 commitDetails.Mu.Unlock() 738 } 739 740 //plan cache 741 if sei.CausetInCache { 742 ssElement.planInCache = true 743 ssElement.planCacheHits += 1 744 } else { 745 ssElement.planInCache = false 746 } 747 748 // other 749 ssElement.sumAffectedRows += sei.StmtCtx.AffectedRows() 750 ssElement.sumMem += sei.MemMax 751 if sei.MemMax > ssElement.maxMem { 752 ssElement.maxMem = sei.MemMax 753 } 754 ssElement.sumDisk += sei.DiskMax 755 if sei.DiskMax > ssElement.maxDisk { 756 ssElement.maxDisk = sei.DiskMax 757 } 758 if sei.StartTime.Before(ssElement.firstSeen) { 759 ssElement.firstSeen = sei.StartTime 760 } 761 if ssElement.lastSeen.Before(sei.StartTime) { 762 ssElement.lastSeen = sei.StartTime 763 } 764 if sei.InterDircRetryCount > 0 { 765 ssElement.execRetryCount += sei.InterDircRetryCount 766 ssElement.execRetryTime += sei.InterDircRetryTime 767 } 768 } 769 770 func (ssElement *stmtSummaryByDigestElement) toCauset(ssbd *stmtSummaryByDigest) []types.Causet { 771 ssElement.Lock() 772 defer ssElement.Unlock() 773 774 plan, err := plancodec.DecodeCauset(ssElement.sampleCauset) 775 if err != nil { 776 logutil.BgLogger().Error("decode plan in memex summary failed", zap.String("plan", ssElement.sampleCauset), zap.Error(err)) 777 plan = "" 778 } 779 780 sampleUser := "" 781 for key := range ssElement.authUsers { 782 sampleUser = key 783 break 784 } 785 786 // Actually, there's a small chance that endTime is out of date, but it's hard to keep it up to date all the time. 787 return types.MakeCausets( 788 types.NewTime(types.FromGoTime(time.Unix(ssElement.beginTime, 0)), allegrosql.TypeTimestamp, 0), 789 types.NewTime(types.FromGoTime(time.Unix(ssElement.endTime, 0)), allegrosql.TypeTimestamp, 0), 790 ssbd.stmtType, 791 ssbd.schemaName, 792 ssbd.digest, 793 ssbd.normalizedALLEGROSQL, 794 convertEmptyToNil(ssbd.blockNames), 795 convertEmptyToNil(strings.Join(ssElement.indexNames, ",")), 796 convertEmptyToNil(sampleUser), 797 ssElement.execCount, 798 ssElement.sumErrors, 799 ssElement.sumWarnings, 800 int64(ssElement.sumLatency), 801 int64(ssElement.maxLatency), 802 int64(ssElement.minLatency), 803 avgInt(int64(ssElement.sumLatency), ssElement.execCount), 804 avgInt(int64(ssElement.sumParseLatency), ssElement.execCount), 805 int64(ssElement.maxParseLatency), 806 avgInt(int64(ssElement.sumCompileLatency), ssElement.execCount), 807 int64(ssElement.maxCompileLatency), 808 ssElement.sumNumCausetTasks, 809 int64(ssElement.maxCopProcessTime), 810 convertEmptyToNil(ssElement.maxCopProcessAddress), 811 int64(ssElement.maxCopWaitTime), 812 convertEmptyToNil(ssElement.maxCopWaitAddress), 813 avgInt(int64(ssElement.sumProcessTime), ssElement.execCount), 814 int64(ssElement.maxProcessTime), 815 avgInt(int64(ssElement.sumWaitTime), ssElement.execCount), 816 int64(ssElement.maxWaitTime), 817 avgInt(int64(ssElement.sumBackoffTime), ssElement.execCount), 818 int64(ssElement.maxBackoffTime), 819 avgInt(ssElement.sumTotalKeys, ssElement.execCount), 820 ssElement.maxTotalKeys, 821 avgInt(ssElement.sumProcessedKeys, ssElement.execCount), 822 ssElement.maxProcessedKeys, 823 avgInt(int64(ssElement.sumPrewriteTime), ssElement.commitCount), 824 int64(ssElement.maxPrewriteTime), 825 avgInt(int64(ssElement.sumCommitTime), ssElement.commitCount), 826 int64(ssElement.maxCommitTime), 827 avgInt(int64(ssElement.sumGetCommitTsTime), ssElement.commitCount), 828 int64(ssElement.maxGetCommitTsTime), 829 avgInt(ssElement.sumCommitBackoffTime, ssElement.commitCount), 830 ssElement.maxCommitBackoffTime, 831 avgInt(ssElement.sumResolveLockTime, ssElement.commitCount), 832 ssElement.maxResolveLockTime, 833 avgInt(int64(ssElement.sumLocalLatchTime), ssElement.commitCount), 834 int64(ssElement.maxLocalLatchTime), 835 avgFloat(ssElement.sumWriteKeys, ssElement.commitCount), 836 ssElement.maxWriteKeys, 837 avgFloat(ssElement.sumWriteSize, ssElement.commitCount), 838 ssElement.maxWriteSize, 839 avgFloat(ssElement.sumPrewriteRegionNum, ssElement.commitCount), 840 int(ssElement.maxPrewriteRegionNum), 841 avgFloat(ssElement.sumTxnRetry, ssElement.commitCount), 842 ssElement.maxTxnRetry, 843 int(ssElement.execRetryCount), 844 int64(ssElement.execRetryTime), 845 ssElement.sumBackoffTimes, 846 formatBackoffTypes(ssElement.backoffTypes), 847 avgInt(ssElement.sumMem, ssElement.execCount), 848 ssElement.maxMem, 849 avgInt(ssElement.sumDisk, ssElement.execCount), 850 ssElement.maxDisk, 851 avgFloat(int64(ssElement.sumAffectedRows), ssElement.execCount), 852 types.NewTime(types.FromGoTime(ssElement.firstSeen), allegrosql.TypeTimestamp, 0), 853 types.NewTime(types.FromGoTime(ssElement.lastSeen), allegrosql.TypeTimestamp, 0), 854 ssElement.planInCache, 855 ssElement.planCacheHits, 856 ssElement.sampleALLEGROSQL, 857 ssElement.prevALLEGROSQL, 858 ssbd.planDigest, 859 plan, 860 ) 861 } 862 863 // Truncate ALLEGROALLEGROSQL to maxALLEGROSQLLength. 864 func formatALLEGROSQL(allegrosql string) string { 865 maxALLEGROSQLLength := StmtSummaryByDigestMap.maxALLEGROSQLLength() 866 length := len(allegrosql) 867 if length > maxALLEGROSQLLength { 868 allegrosql = fmt.Sprintf("%.*s(len:%d)", maxALLEGROSQLLength, allegrosql, length) 869 } 870 return allegrosql 871 } 872 873 // Format the backoffType map to a string or nil. 874 func formatBackoffTypes(backoffMap map[fmt.Stringer]int) interface{} { 875 type backoffStat struct { 876 backoffType fmt.Stringer 877 count int 878 } 879 880 size := len(backoffMap) 881 if size == 0 { 882 return nil 883 } 884 885 backoffArray := make([]backoffStat, 0, len(backoffMap)) 886 for backoffType, count := range backoffMap { 887 backoffArray = append(backoffArray, backoffStat{backoffType, count}) 888 } 889 sort.Slice(backoffArray, func(i, j int) bool { 890 return backoffArray[i].count > backoffArray[j].count 891 }) 892 893 var buffer bytes.Buffer 894 for index, stat := range backoffArray { 895 if _, err := fmt.Fprintf(&buffer, "%v:%d", stat.backoffType, stat.count); err != nil { 896 return "FORMAT ERROR" 897 } 898 if index < len(backoffArray)-1 { 899 buffer.WriteString(",") 900 } 901 } 902 return buffer.String() 903 } 904 905 func avgInt(sum int64, count int64) int64 { 906 if count > 0 { 907 return sum / count 908 } 909 return 0 910 } 911 912 func avgFloat(sum int64, count int64) float64 { 913 if count > 0 { 914 return float64(sum) / float64(count) 915 } 916 return 0 917 } 918 919 func convertEmptyToNil(str string) interface{} { 920 if str == "" { 921 return nil 922 } 923 return str 924 }