github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/petri/topn_slow_query.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 petri
    15  
    16  import (
    17  	"container/heap"
    18  	"sort"
    19  	"sync"
    20  	"time"
    21  
    22  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    23  	"github.com/whtcorpsinc/milevadb/soliton/execdetails"
    24  )
    25  
    26  type slowQueryHeap struct {
    27  	data []*SlowQueryInfo
    28  }
    29  
    30  func (h *slowQueryHeap) Len() int           { return len(h.data) }
    31  func (h *slowQueryHeap) Less(i, j int) bool { return h.data[i].Duration < h.data[j].Duration }
    32  func (h *slowQueryHeap) Swap(i, j int)      { h.data[i], h.data[j] = h.data[j], h.data[i] }
    33  
    34  func (h *slowQueryHeap) Push(x interface{}) {
    35  	h.data = append(h.data, x.(*SlowQueryInfo))
    36  }
    37  
    38  func (h *slowQueryHeap) Pop() interface{} {
    39  	old := h.data
    40  	n := len(old)
    41  	x := old[n-1]
    42  	h.data = old[0 : n-1]
    43  	return x
    44  }
    45  
    46  func (h *slowQueryHeap) RemoveExpired(now time.Time, period time.Duration) {
    47  	// Remove outdated slow query element.
    48  	idx := 0
    49  	for i := 0; i < len(h.data); i++ {
    50  		outdateTime := h.data[i].Start.Add(period)
    51  		if outdateTime.After(now) {
    52  			h.data[idx] = h.data[i]
    53  			idx++
    54  		}
    55  	}
    56  	if len(h.data) == idx {
    57  		return
    58  	}
    59  
    60  	// Rebuild the heap.
    61  	h.data = h.data[:idx]
    62  	heap.Init(h)
    63  }
    64  
    65  func (h *slowQueryHeap) Query(count int) []*SlowQueryInfo {
    66  	// The sorted array still maintains the heap property.
    67  	sort.Sort(h)
    68  
    69  	// The result should be in decrease order.
    70  	return takeLastN(h.data, count)
    71  }
    72  
    73  type slowQueryQueue struct {
    74  	data []*SlowQueryInfo
    75  	size int
    76  }
    77  
    78  func (q *slowQueryQueue) Enqueue(info *SlowQueryInfo) {
    79  	if len(q.data) < q.size {
    80  		q.data = append(q.data, info)
    81  		return
    82  	}
    83  
    84  	q.data = append(q.data, info)[1:]
    85  }
    86  
    87  func (q *slowQueryQueue) Query(count int) []*SlowQueryInfo {
    88  	// Queue is empty.
    89  	if len(q.data) == 0 {
    90  		return nil
    91  	}
    92  	return takeLastN(q.data, count)
    93  }
    94  
    95  func takeLastN(data []*SlowQueryInfo, count int) []*SlowQueryInfo {
    96  	if count > len(data) {
    97  		count = len(data)
    98  	}
    99  	ret := make([]*SlowQueryInfo, 0, count)
   100  	for i := len(data) - 1; i >= 0 && len(ret) < count; i-- {
   101  		ret = append(ret, data[i])
   102  	}
   103  	return ret
   104  }
   105  
   106  // topNSlowQueries maintains two heaps to causetstore recent slow queries: one for user's and one for internal.
   107  // N = 30, period = 7 days by default.
   108  // It also maintains a recent queue, in a FIFO manner.
   109  type topNSlowQueries struct {
   110  	recent   slowQueryQueue
   111  	user     slowQueryHeap
   112  	internal slowQueryHeap
   113  	topN     int
   114  	period   time.Duration
   115  	ch       chan *SlowQueryInfo
   116  	msgCh    chan *showSlowMessage
   117  
   118  	mu struct {
   119  		sync.RWMutex
   120  		closed bool
   121  	}
   122  }
   123  
   124  func newTopNSlowQueries(topN int, period time.Duration, queueSize int) *topNSlowQueries {
   125  	ret := &topNSlowQueries{
   126  		topN:   topN,
   127  		period: period,
   128  		ch:     make(chan *SlowQueryInfo, 1000),
   129  		msgCh:  make(chan *showSlowMessage, 10),
   130  	}
   131  	ret.user.data = make([]*SlowQueryInfo, 0, topN)
   132  	ret.internal.data = make([]*SlowQueryInfo, 0, topN)
   133  	ret.recent.size = queueSize
   134  	ret.recent.data = make([]*SlowQueryInfo, 0, queueSize)
   135  	return ret
   136  }
   137  
   138  func (q *topNSlowQueries) Append(info *SlowQueryInfo) {
   139  	// Put into the recent queue.
   140  	q.recent.Enqueue(info)
   141  
   142  	var h *slowQueryHeap
   143  	if info.Internal {
   144  		h = &q.internal
   145  	} else {
   146  		h = &q.user
   147  	}
   148  
   149  	// Heap is not full.
   150  	if len(h.data) < q.topN {
   151  		heap.Push(h, info)
   152  		return
   153  	}
   154  
   155  	// Replace the heap top.
   156  	if info.Duration > h.data[0].Duration {
   157  		heap.Pop(h)
   158  		heap.Push(h, info)
   159  	}
   160  }
   161  
   162  func (q *topNSlowQueries) QueryAll() []*SlowQueryInfo {
   163  	return q.recent.data
   164  }
   165  
   166  func (q *topNSlowQueries) RemoveExpired(now time.Time) {
   167  	q.user.RemoveExpired(now, q.period)
   168  	q.internal.RemoveExpired(now, q.period)
   169  }
   170  
   171  type showSlowMessage struct {
   172  	request *ast.ShowSlow
   173  	result  []*SlowQueryInfo
   174  	sync.WaitGroup
   175  }
   176  
   177  func (q *topNSlowQueries) QueryRecent(count int) []*SlowQueryInfo {
   178  	return q.recent.Query(count)
   179  }
   180  
   181  func (q *topNSlowQueries) QueryTop(count int, HoTT ast.ShowSlowHoTT) []*SlowQueryInfo {
   182  	var ret []*SlowQueryInfo
   183  	switch HoTT {
   184  	case ast.ShowSlowHoTTDefault:
   185  		ret = q.user.Query(count)
   186  	case ast.ShowSlowHoTTInternal:
   187  		ret = q.internal.Query(count)
   188  	case ast.ShowSlowHoTTAll:
   189  		tmp := make([]*SlowQueryInfo, 0, len(q.user.data)+len(q.internal.data))
   190  		tmp = append(tmp, q.user.data...)
   191  		tmp = append(tmp, q.internal.data...)
   192  		tmp1 := slowQueryHeap{tmp}
   193  		sort.Sort(&tmp1)
   194  		ret = takeLastN(tmp, count)
   195  	}
   196  	return ret
   197  }
   198  
   199  func (q *topNSlowQueries) Close() {
   200  	q.mu.Lock()
   201  	q.mu.closed = true
   202  	q.mu.Unlock()
   203  
   204  	close(q.ch)
   205  }
   206  
   207  // SlowQueryInfo is a struct to record slow query info.
   208  type SlowQueryInfo struct {
   209  	ALLEGROALLEGROSQL        string
   210  	Start      time.Time
   211  	Duration   time.Duration
   212  	Detail     execdetails.InterDircDetails
   213  	ConnID     uint64
   214  	TxnTS      uint64
   215  	User       string
   216  	EDB         string
   217  	BlockIDs   string
   218  	IndexNames string
   219  	Digest     string
   220  	Internal   bool
   221  	Succ       bool
   222  }