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  }