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 }