github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/schemareplicant/perfschema/tables.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 perfschema 15 16 import ( 17 "fmt" 18 "net/http" 19 "sort" 20 "strings" 21 "sync" 22 "time" 23 24 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 25 "github.com/whtcorpsinc/BerolinaSQL/terror" 26 "github.com/whtcorpsinc/errors" 27 "github.com/whtcorpsinc/failpoint" 28 "github.com/whtcorpsinc/milevadb/causet" 29 "github.com/whtcorpsinc/milevadb/ekv" 30 "github.com/whtcorpsinc/milevadb/schemareplicant" 31 "github.com/whtcorpsinc/milevadb/soliton" 32 "github.com/whtcorpsinc/milevadb/soliton/profile" 33 "github.com/whtcorpsinc/milevadb/spacetime/autoid" 34 "github.com/whtcorpsinc/milevadb/stochastikctx" 35 "github.com/whtcorpsinc/milevadb/types" 36 ) 37 38 const ( 39 blockNameGlobalStatus = "global_status" 40 blockNameStochastikStatus = "stochastik_status" 41 blockNameSetupActors = "setup_actors" 42 blockNameSetupObjects = "setup_objects" 43 blockNameSetupInstruments = "setup_instruments" 44 blockNameSetupConsumers = "setup_consumers" 45 blockNameEventsStatementsCurrent = "events_memexs_current" 46 blockNameEventsStatementsHistory = "events_memexs_history" 47 blockNameEventsStatementsHistoryLong = "events_memexs_history_long" 48 blockNamePreparedStatementsInstances = "prepared_memexs_instances" 49 blockNameEventsTransactionsCurrent = "events_transactions_current" 50 blockNameEventsTransactionsHistory = "events_transactions_history" 51 blockNameEventsTransactionsHistoryLong = "events_transactions_history_long" 52 blockNameEventsStagesCurrent = "events_stages_current" 53 blockNameEventsStagesHistory = "events_stages_history" 54 blockNameEventsStagesHistoryLong = "events_stages_history_long" 55 blockNameEventsStatementsSummaryByDigest = "events_memexs_summary_by_digest" 56 blockNameMilevaDBProfileCPU = "milevadb_profile_cpu" 57 blockNameMilevaDBProfileMemory = "milevadb_profile_memory" 58 blockNameMilevaDBProfileMutex = "milevadb_profile_mutex" 59 blockNameMilevaDBProfileAllocs = "milevadb_profile_allocs" 60 blockNameMilevaDBProfileBlock = "milevadb_profile_block" 61 blockNameMilevaDBProfileGoroutines = "milevadb_profile_goroutines" 62 blockNameEinsteinDBProfileCPU = "einsteindb_profile_cpu" 63 blockNameFIDelProfileCPU = "FIDel_profile_cpu" 64 blockNameFIDelProfileMemory = "FIDel_profile_memory" 65 blockNameFIDelProfileMutex = "FIDel_profile_mutex" 66 blockNameFIDelProfileAllocs = "FIDel_profile_allocs" 67 blockNameFIDelProfileBlock = "FIDel_profile_block" 68 blockNameFIDelProfileGoroutines = "FIDel_profile_goroutines" 69 ) 70 71 var blockIDMap = map[string]int64{ 72 blockNameGlobalStatus: autoid.PerformanceSchemaDBID + 1, 73 blockNameStochastikStatus: autoid.PerformanceSchemaDBID + 2, 74 blockNameSetupActors: autoid.PerformanceSchemaDBID + 3, 75 blockNameSetupObjects: autoid.PerformanceSchemaDBID + 4, 76 blockNameSetupInstruments: autoid.PerformanceSchemaDBID + 5, 77 blockNameSetupConsumers: autoid.PerformanceSchemaDBID + 6, 78 blockNameEventsStatementsCurrent: autoid.PerformanceSchemaDBID + 7, 79 blockNameEventsStatementsHistory: autoid.PerformanceSchemaDBID + 8, 80 blockNameEventsStatementsHistoryLong: autoid.PerformanceSchemaDBID + 9, 81 blockNamePreparedStatementsInstances: autoid.PerformanceSchemaDBID + 10, 82 blockNameEventsTransactionsCurrent: autoid.PerformanceSchemaDBID + 11, 83 blockNameEventsTransactionsHistory: autoid.PerformanceSchemaDBID + 12, 84 blockNameEventsTransactionsHistoryLong: autoid.PerformanceSchemaDBID + 13, 85 blockNameEventsStagesCurrent: autoid.PerformanceSchemaDBID + 14, 86 blockNameEventsStagesHistory: autoid.PerformanceSchemaDBID + 15, 87 blockNameEventsStagesHistoryLong: autoid.PerformanceSchemaDBID + 16, 88 blockNameEventsStatementsSummaryByDigest: autoid.PerformanceSchemaDBID + 17, 89 blockNameMilevaDBProfileCPU: autoid.PerformanceSchemaDBID + 18, 90 blockNameMilevaDBProfileMemory: autoid.PerformanceSchemaDBID + 19, 91 blockNameMilevaDBProfileMutex: autoid.PerformanceSchemaDBID + 20, 92 blockNameMilevaDBProfileAllocs: autoid.PerformanceSchemaDBID + 21, 93 blockNameMilevaDBProfileBlock: autoid.PerformanceSchemaDBID + 22, 94 blockNameMilevaDBProfileGoroutines: autoid.PerformanceSchemaDBID + 23, 95 blockNameEinsteinDBProfileCPU: autoid.PerformanceSchemaDBID + 24, 96 blockNameFIDelProfileCPU: autoid.PerformanceSchemaDBID + 25, 97 blockNameFIDelProfileMemory: autoid.PerformanceSchemaDBID + 26, 98 blockNameFIDelProfileMutex: autoid.PerformanceSchemaDBID + 27, 99 blockNameFIDelProfileAllocs: autoid.PerformanceSchemaDBID + 28, 100 blockNameFIDelProfileBlock: autoid.PerformanceSchemaDBID + 29, 101 blockNameFIDelProfileGoroutines: autoid.PerformanceSchemaDBID + 30, 102 } 103 104 // perfSchemaBlock stands for the fake causet all its data is in the memory. 105 type perfSchemaBlock struct { 106 schemareplicant.VirtualBlock 107 spacetime *perceptron.BlockInfo 108 defcaus []*causet.DeferredCauset 109 tp causet.Type 110 } 111 112 var pluginBlock = make(map[string]func(autoid.SlabPredictors, *perceptron.BlockInfo) (causet.Block, error)) 113 114 // IsPredefinedBlock judges whether this causet is predefined. 115 func IsPredefinedBlock(blockName string) bool { 116 _, ok := blockIDMap[strings.ToLower(blockName)] 117 return ok 118 } 119 120 // RegisterBlock registers a new causet into MilevaDB. 121 func RegisterBlock(blockName, allegrosql string, 122 blockFromMeta func(autoid.SlabPredictors, *perceptron.BlockInfo) (causet.Block, error)) { 123 perfSchemaBlocks = append(perfSchemaBlocks, allegrosql) 124 pluginBlock[blockName] = blockFromMeta 125 } 126 127 func blockFromMeta(allocs autoid.SlabPredictors, spacetime *perceptron.BlockInfo) (causet.Block, error) { 128 if f, ok := pluginBlock[spacetime.Name.L]; ok { 129 ret, err := f(allocs, spacetime) 130 return ret, err 131 } 132 return createPerfSchemaBlock(spacetime), nil 133 } 134 135 // createPerfSchemaBlock creates all perfSchemaBlocks 136 func createPerfSchemaBlock(spacetime *perceptron.BlockInfo) *perfSchemaBlock { 137 defCausumns := make([]*causet.DeferredCauset, 0, len(spacetime.DeferredCausets)) 138 for _, defCausInfo := range spacetime.DeferredCausets { 139 defCaus := causet.ToDeferredCauset(defCausInfo) 140 defCausumns = append(defCausumns, defCaus) 141 } 142 tp := causet.VirtualBlock 143 t := &perfSchemaBlock{ 144 spacetime: spacetime, 145 defcaus: defCausumns, 146 tp: tp, 147 } 148 return t 149 } 150 151 // DefCauss implements causet.Block Type interface. 152 func (vt *perfSchemaBlock) DefCauss() []*causet.DeferredCauset { 153 return vt.defcaus 154 } 155 156 // VisibleDefCauss implements causet.Block VisibleDefCauss interface. 157 func (vt *perfSchemaBlock) VisibleDefCauss() []*causet.DeferredCauset { 158 return vt.defcaus 159 } 160 161 // HiddenDefCauss implements causet.Block HiddenDefCauss interface. 162 func (vt *perfSchemaBlock) HiddenDefCauss() []*causet.DeferredCauset { 163 return nil 164 } 165 166 // WriblockDefCauss implements causet.Block Type interface. 167 func (vt *perfSchemaBlock) WriblockDefCauss() []*causet.DeferredCauset { 168 return vt.defcaus 169 } 170 171 // FullHiddenDefCaussAndVisibleDefCauss implements causet FullHiddenDefCaussAndVisibleDefCauss interface. 172 func (vt *perfSchemaBlock) FullHiddenDefCaussAndVisibleDefCauss() []*causet.DeferredCauset { 173 return vt.defcaus 174 } 175 176 // GetID implements causet.Block GetID interface. 177 func (vt *perfSchemaBlock) GetPhysicalID() int64 { 178 return vt.spacetime.ID 179 } 180 181 // Meta implements causet.Block Type interface. 182 func (vt *perfSchemaBlock) Meta() *perceptron.BlockInfo { 183 return vt.spacetime 184 } 185 186 // Type implements causet.Block Type interface. 187 func (vt *perfSchemaBlock) Type() causet.Type { 188 return vt.tp 189 } 190 191 func (vt *perfSchemaBlock) getRows(ctx stochastikctx.Context, defcaus []*causet.DeferredCauset) (fullRows [][]types.Causet, err error) { 192 switch vt.spacetime.Name.O { 193 case blockNameMilevaDBProfileCPU: 194 fullRows, err = (&profile.DefCauslector{}).ProfileGraph("cpu") 195 case blockNameMilevaDBProfileMemory: 196 fullRows, err = (&profile.DefCauslector{}).ProfileGraph("heap") 197 case blockNameMilevaDBProfileMutex: 198 fullRows, err = (&profile.DefCauslector{}).ProfileGraph("mutex") 199 case blockNameMilevaDBProfileAllocs: 200 fullRows, err = (&profile.DefCauslector{}).ProfileGraph("allocs") 201 case blockNameMilevaDBProfileBlock: 202 fullRows, err = (&profile.DefCauslector{}).ProfileGraph("causet") 203 case blockNameMilevaDBProfileGoroutines: 204 fullRows, err = (&profile.DefCauslector{}).ProfileGraph("goroutine") 205 case blockNameEinsteinDBProfileCPU: 206 interval := fmt.Sprintf("%d", profile.CPUProfileInterval/time.Second) 207 fullRows, err = dataForRemoteProfile(ctx, "einsteindb", "/debug/pprof/profile?seconds="+interval, false) 208 case blockNameFIDelProfileCPU: 209 interval := fmt.Sprintf("%d", profile.CPUProfileInterval/time.Second) 210 fullRows, err = dataForRemoteProfile(ctx, "fidel", "/fidel/api/v1/debug/pprof/profile?seconds="+interval, false) 211 case blockNameFIDelProfileMemory: 212 fullRows, err = dataForRemoteProfile(ctx, "fidel", "/fidel/api/v1/debug/pprof/heap", false) 213 case blockNameFIDelProfileMutex: 214 fullRows, err = dataForRemoteProfile(ctx, "fidel", "/fidel/api/v1/debug/pprof/mutex", false) 215 case blockNameFIDelProfileAllocs: 216 fullRows, err = dataForRemoteProfile(ctx, "fidel", "/fidel/api/v1/debug/pprof/allocs", false) 217 case blockNameFIDelProfileBlock: 218 fullRows, err = dataForRemoteProfile(ctx, "fidel", "/fidel/api/v1/debug/pprof/causet", false) 219 case blockNameFIDelProfileGoroutines: 220 fullRows, err = dataForRemoteProfile(ctx, "fidel", "/fidel/api/v1/debug/pprof/goroutine?debug=2", true) 221 } 222 if err != nil { 223 return 224 } 225 if len(defcaus) == len(vt.defcaus) { 226 return 227 } 228 rows := make([][]types.Causet, len(fullRows)) 229 for i, fullRow := range fullRows { 230 event := make([]types.Causet, len(defcaus)) 231 for j, defCaus := range defcaus { 232 event[j] = fullRow[defCaus.Offset] 233 } 234 rows[i] = event 235 } 236 return rows, nil 237 } 238 239 // IterRecords implements causet.Block IterRecords interface. 240 func (vt *perfSchemaBlock) IterRecords(ctx stochastikctx.Context, startKey ekv.Key, defcaus []*causet.DeferredCauset, 241 fn causet.RecordIterFunc) error { 242 if len(startKey) != 0 { 243 return causet.ErrUnsupportedOp 244 } 245 rows, err := vt.getRows(ctx, defcaus) 246 if err != nil { 247 return err 248 } 249 for i, event := range rows { 250 more, err := fn(ekv.IntHandle(i), event, defcaus) 251 if err != nil { 252 return err 253 } 254 if !more { 255 break 256 } 257 } 258 return nil 259 } 260 261 func dataForRemoteProfile(ctx stochastikctx.Context, nodeType, uri string, isGoroutine bool) ([][]types.Causet, error) { 262 var ( 263 servers []schemareplicant.ServerInfo 264 err error 265 ) 266 switch nodeType { 267 case "einsteindb": 268 servers, err = schemareplicant.GetStoreServerInfo(ctx) 269 case "fidel": 270 servers, err = schemareplicant.GetFIDelServerInfo(ctx) 271 default: 272 return nil, errors.Errorf("%s does not support profile remote component", nodeType) 273 } 274 failpoint.Inject("mockRemoteNodeStatusAddress", func(val failpoint.Value) { 275 // The cluster topology is injected by `failpoint` memex and 276 // there is no extra checks for it. (let the test fail if the memex invalid) 277 if s := val.(string); len(s) > 0 { 278 servers = servers[:0] 279 for _, server := range strings.Split(s, ";") { 280 parts := strings.Split(server, ",") 281 if parts[0] != nodeType { 282 continue 283 } 284 servers = append(servers, schemareplicant.ServerInfo{ 285 ServerType: parts[0], 286 Address: parts[1], 287 StatusAddr: parts[2], 288 }) 289 } 290 // erase error 291 err = nil 292 } 293 }) 294 if err != nil { 295 return nil, errors.Trace(err) 296 } 297 298 type result struct { 299 addr string 300 rows [][]types.Causet 301 err error 302 } 303 304 wg := sync.WaitGroup{} 305 ch := make(chan result, len(servers)) 306 for _, server := range servers { 307 statusAddr := server.StatusAddr 308 if len(statusAddr) == 0 { 309 ctx.GetStochastikVars().StmtCtx.AppendWarning(errors.Errorf("EinsteinDB node %s does not contain status address", server.Address)) 310 continue 311 } 312 313 wg.Add(1) 314 go func(address string) { 315 soliton.WithRecovery(func() { 316 defer wg.Done() 317 url := fmt.Sprintf("%s://%s%s", soliton.InternalHTTPSchema(), statusAddr, uri) 318 req, err := http.NewRequest(http.MethodGet, url, nil) 319 if err != nil { 320 ch <- result{err: errors.Trace(err)} 321 return 322 } 323 // Forbidden FIDel follower proxy 324 req.Header.Add("FIDel-Allow-follower-handle", "true") 325 // EinsteinDB output svg format in default 326 req.Header.Add("Content-Type", "application/protobuf") 327 resp, err := soliton.InternalHTTPClient().Do(req) 328 if err != nil { 329 ch <- result{err: errors.Trace(err)} 330 return 331 } 332 defer func() { 333 terror.Log(resp.Body.Close()) 334 }() 335 if resp.StatusCode != http.StatusOK { 336 ch <- result{err: errors.Errorf("request %s failed: %s", url, resp.Status)} 337 return 338 } 339 defCauslector := profile.DefCauslector{} 340 var rows [][]types.Causet 341 if isGoroutine { 342 rows, err = defCauslector.ParseGoroutines(resp.Body) 343 } else { 344 rows, err = defCauslector.ProfileReaderToCausets(resp.Body) 345 } 346 if err != nil { 347 ch <- result{err: errors.Trace(err)} 348 return 349 } 350 ch <- result{addr: address, rows: rows} 351 }, nil) 352 }(statusAddr) 353 } 354 355 wg.Wait() 356 close(ch) 357 358 // Keep the original order to make the result more sblock 359 var results []result 360 for result := range ch { 361 if result.err != nil { 362 ctx.GetStochastikVars().StmtCtx.AppendWarning(result.err) 363 continue 364 } 365 results = append(results, result) 366 } 367 sort.Slice(results, func(i, j int) bool { return results[i].addr < results[j].addr }) 368 var finalRows [][]types.Causet 369 for _, result := range results { 370 addr := types.NewStringCauset(result.addr) 371 for _, event := range result.rows { 372 // Insert the node address in front of rows 373 finalRows = append(finalRows, append([]types.Causet{addr}, event...)) 374 } 375 } 376 return finalRows, nil 377 }