mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	* Dropped unused codekit config * Integrated dynamic and static bindata for public * Ignore public bindata * Add a general generate make task * Integrated flexible public assets into web command * Updated vendoring, added all missiong govendor deps * Made the linter happy with the bindata and dynamic code * Moved public bindata definition to modules directory * Ignoring the new bindata path now * Updated to the new public modules import path * Updated public bindata command and drop the new prefix
		
			
				
	
	
		
			329 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			329 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2016 PingCAP, Inc.
 | 
						|
//
 | 
						|
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
// you may not use this file except in compliance with the License.
 | 
						|
// You may obtain a copy of the License at
 | 
						|
//
 | 
						|
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
// See the License for the specific language governing permissions and
 | 
						|
// limitations under the License.
 | 
						|
 | 
						|
package perfschema
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
	"runtime"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/juju/errors"
 | 
						|
	"github.com/ngaut/log"
 | 
						|
	"github.com/pingcap/tidb/ast"
 | 
						|
	"github.com/pingcap/tidb/kv"
 | 
						|
	"github.com/pingcap/tidb/terror"
 | 
						|
	"github.com/pingcap/tidb/util/types"
 | 
						|
)
 | 
						|
 | 
						|
// statementInfo defines statement instrument information.
 | 
						|
type statementInfo struct {
 | 
						|
	// The registered statement key
 | 
						|
	key uint64
 | 
						|
	// The name of the statement instrument to register
 | 
						|
	name string
 | 
						|
}
 | 
						|
 | 
						|
// StatementState provides temporary storage to a statement runtime statistics.
 | 
						|
// TODO:
 | 
						|
// 1. support statement digest.
 | 
						|
// 2. support prepared statement.
 | 
						|
type StatementState struct {
 | 
						|
	// Connection identifier
 | 
						|
	connID uint64
 | 
						|
	// Statement information
 | 
						|
	info *statementInfo
 | 
						|
	// Statement type
 | 
						|
	stmtType reflect.Type
 | 
						|
	// Source file and line number
 | 
						|
	source string
 | 
						|
	// Timer name
 | 
						|
	timerName enumTimerName
 | 
						|
	// Timer start
 | 
						|
	timerStart int64
 | 
						|
	// Timer end
 | 
						|
	timerEnd int64
 | 
						|
	// Locked time
 | 
						|
	lockTime int64
 | 
						|
	// SQL statement string
 | 
						|
	sqlText string
 | 
						|
	// Current schema name
 | 
						|
	schemaName string
 | 
						|
	// Number of errors
 | 
						|
	errNum uint32
 | 
						|
	// Number of warnings
 | 
						|
	warnNum uint32
 | 
						|
	// Rows affected
 | 
						|
	rowsAffected uint64
 | 
						|
	// Rows sent
 | 
						|
	rowsSent uint64
 | 
						|
	// Rows examined
 | 
						|
	rowsExamined uint64
 | 
						|
	// Metric, temporary tables created on disk
 | 
						|
	createdTmpDiskTables uint32
 | 
						|
	// Metric, temproray tables created
 | 
						|
	createdTmpTables uint32
 | 
						|
	// Metric, number of select full join
 | 
						|
	selectFullJoin uint32
 | 
						|
	// Metric, number of select full range join
 | 
						|
	selectFullRangeJoin uint32
 | 
						|
	// Metric, number of select range
 | 
						|
	selectRange uint32
 | 
						|
	// Metric, number of select range check
 | 
						|
	selectRangeCheck uint32
 | 
						|
	// Metric, number of select scan
 | 
						|
	selectScan uint32
 | 
						|
	// Metric, number of sort merge passes
 | 
						|
	sortMergePasses uint32
 | 
						|
	// Metric, number of sort merge
 | 
						|
	sortRange uint32
 | 
						|
	// Metric, number of sort rows
 | 
						|
	sortRows uint32
 | 
						|
	// Metric, number of sort scans
 | 
						|
	sortScan uint32
 | 
						|
	// Metric, no index used flag
 | 
						|
	noIndexUsed uint8
 | 
						|
	// Metric, no good index used flag
 | 
						|
	noGoodIndexUsed uint8
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	// Maximum allowed number of elements in table events_statements_history.
 | 
						|
	// TODO: make it configurable?
 | 
						|
	stmtsHistoryElemMax int = 1024
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	stmtInfos = make(map[reflect.Type]*statementInfo)
 | 
						|
)
 | 
						|
 | 
						|
func (ps *perfSchema) RegisterStatement(category, name string, elem interface{}) {
 | 
						|
	instrumentName := fmt.Sprintf("%s%s/%s", statementInstrumentPrefix, category, name)
 | 
						|
	key, err := ps.addInstrument(instrumentName)
 | 
						|
	if err != nil {
 | 
						|
		// just ignore, do nothing else.
 | 
						|
		log.Errorf("Unable to register instrument %s", instrumentName)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	stmtInfos[reflect.TypeOf(elem)] = &statementInfo{
 | 
						|
		key:  key,
 | 
						|
		name: instrumentName,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (ps *perfSchema) StartStatement(sql string, connID uint64, callerName EnumCallerName, elem interface{}) *StatementState {
 | 
						|
	stmtType := reflect.TypeOf(elem)
 | 
						|
	info, ok := stmtInfos[stmtType]
 | 
						|
	if !ok {
 | 
						|
		// just ignore, do nothing else.
 | 
						|
		log.Errorf("No instrument registered for statement %s", stmtType)
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// check and apply the configuration parameter in table setup_timers.
 | 
						|
	timerName, err := ps.getTimerName(flagStatement)
 | 
						|
	if err != nil {
 | 
						|
		// just ignore, do nothing else.
 | 
						|
		log.Error("Unable to check setup_timers table")
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	var timerStart int64
 | 
						|
	switch timerName {
 | 
						|
	case timerNameNanosec:
 | 
						|
		timerStart = time.Now().UnixNano()
 | 
						|
	case timerNameMicrosec:
 | 
						|
		timerStart = time.Now().UnixNano() / int64(time.Microsecond)
 | 
						|
	case timerNameMillisec:
 | 
						|
		timerStart = time.Now().UnixNano() / int64(time.Millisecond)
 | 
						|
	default:
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// TODO: check and apply the additional configuration parameters in:
 | 
						|
	// - table setup_actors
 | 
						|
	// - table setup_setup_consumers
 | 
						|
	// - table setup_instruments
 | 
						|
	// - table setup_objects
 | 
						|
 | 
						|
	var source string
 | 
						|
	source, ok = callerNames[callerName]
 | 
						|
	if !ok {
 | 
						|
		_, fileName, fileLine, ok := runtime.Caller(1)
 | 
						|
		if !ok {
 | 
						|
			// just ignore, do nothing else.
 | 
						|
			log.Error("Unable to get runtime.Caller(1)")
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
		source = fmt.Sprintf("%s:%d", fileName, fileLine)
 | 
						|
		callerNames[callerName] = source
 | 
						|
	}
 | 
						|
 | 
						|
	return &StatementState{
 | 
						|
		connID:     connID,
 | 
						|
		info:       info,
 | 
						|
		stmtType:   stmtType,
 | 
						|
		source:     source,
 | 
						|
		timerName:  timerName,
 | 
						|
		timerStart: timerStart,
 | 
						|
		sqlText:    sql,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (ps *perfSchema) EndStatement(state *StatementState) {
 | 
						|
	if state == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	switch state.timerName {
 | 
						|
	case timerNameNanosec:
 | 
						|
		state.timerEnd = time.Now().UnixNano()
 | 
						|
	case timerNameMicrosec:
 | 
						|
		state.timerEnd = time.Now().UnixNano() / int64(time.Microsecond)
 | 
						|
	case timerNameMillisec:
 | 
						|
		state.timerEnd = time.Now().UnixNano() / int64(time.Millisecond)
 | 
						|
	default:
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	log.Debugf("EndStatement: sql %s, connection id %d, type %s", state.sqlText, state.connID, state.stmtType)
 | 
						|
 | 
						|
	record := state2Record(state)
 | 
						|
	err := ps.updateEventsStmtsCurrent(state.connID, record)
 | 
						|
	if err != nil {
 | 
						|
		log.Error("Unable to update events_statements_current table")
 | 
						|
	}
 | 
						|
	err = ps.appendEventsStmtsHistory(record)
 | 
						|
	if err != nil {
 | 
						|
		log.Errorf("Unable to append to events_statements_history table %v", errors.ErrorStack(err))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func state2Record(state *StatementState) []types.Datum {
 | 
						|
	return types.MakeDatums(
 | 
						|
		state.connID,             // THREAD_ID
 | 
						|
		state.info.key,           // EVENT_ID
 | 
						|
		nil,                      // END_EVENT_ID
 | 
						|
		state.info.name,          // EVENT_NAME
 | 
						|
		state.source,             // SOURCE
 | 
						|
		uint64(state.timerStart), // TIMER_START
 | 
						|
		uint64(state.timerEnd),   // TIMER_END
 | 
						|
		nil, // TIMER_WAIT
 | 
						|
		uint64(state.lockTime),             // LOCK_TIME
 | 
						|
		state.sqlText,                      // SQL_TEXT
 | 
						|
		nil,                                // DIGEST
 | 
						|
		nil,                                // DIGEST_TEXT
 | 
						|
		state.schemaName,                   // CURRENT_SCHEMA
 | 
						|
		nil,                                // OBJECT_TYPE
 | 
						|
		nil,                                // OBJECT_SCHEMA
 | 
						|
		nil,                                // OBJECT_NAME
 | 
						|
		nil,                                // OBJECT_INSTANCE_BEGIN
 | 
						|
		nil,                                // MYSQL_ERRNO,
 | 
						|
		nil,                                // RETURNED_SQLSTATE
 | 
						|
		nil,                                // MESSAGE_TEXT
 | 
						|
		uint64(state.errNum),               // ERRORS
 | 
						|
		uint64(state.warnNum),              // WARNINGS
 | 
						|
		state.rowsAffected,                 // ROWS_AFFECTED
 | 
						|
		state.rowsSent,                     // ROWS_SENT
 | 
						|
		state.rowsExamined,                 // ROWS_EXAMINED
 | 
						|
		uint64(state.createdTmpDiskTables), // CREATED_TMP_DISK_TABLES
 | 
						|
		uint64(state.createdTmpTables),     // CREATED_TMP_TABLES
 | 
						|
		uint64(state.selectFullJoin),       // SELECT_FULL_JOIN
 | 
						|
		uint64(state.selectFullRangeJoin),  // SELECT_FULL_RANGE_JOIN
 | 
						|
		uint64(state.selectRange),          // SELECT_RANGE
 | 
						|
		uint64(state.selectRangeCheck),     // SELECT_RANGE_CHECK
 | 
						|
		uint64(state.selectScan),           // SELECT_SCAN
 | 
						|
		uint64(state.sortMergePasses),      // SORT_MERGE_PASSES
 | 
						|
		uint64(state.sortRange),            // SORT_RANGE
 | 
						|
		uint64(state.sortRows),             // SORT_ROWS
 | 
						|
		uint64(state.sortScan),             // SORT_SCAN
 | 
						|
		uint64(state.noIndexUsed),          // NO_INDEX_USED
 | 
						|
		uint64(state.noGoodIndexUsed),      // NO_GOOD_INDEX_USED
 | 
						|
		nil, // NESTING_EVENT_ID
 | 
						|
		nil, // NESTING_EVENT_TYPE
 | 
						|
		nil, // NESTING_EVENT_LEVEL
 | 
						|
	)
 | 
						|
}
 | 
						|
 | 
						|
func (ps *perfSchema) updateEventsStmtsCurrent(connID uint64, record []types.Datum) error {
 | 
						|
	// Try AddRecord
 | 
						|
	tbl := ps.mTables[TableStmtsCurrent]
 | 
						|
	_, err := tbl.AddRecord(nil, record)
 | 
						|
	if err == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	if terror.ErrorNotEqual(err, kv.ErrKeyExists) {
 | 
						|
		return errors.Trace(err)
 | 
						|
	}
 | 
						|
	// Update it
 | 
						|
	handle := int64(connID)
 | 
						|
	err = tbl.UpdateRecord(nil, handle, nil, record, nil)
 | 
						|
	return errors.Trace(err)
 | 
						|
}
 | 
						|
 | 
						|
func (ps *perfSchema) appendEventsStmtsHistory(record []types.Datum) error {
 | 
						|
	tbl := ps.mTables[TableStmtsHistory]
 | 
						|
	if len(ps.historyHandles) < stmtsHistoryElemMax {
 | 
						|
		h, err := tbl.AddRecord(nil, record)
 | 
						|
		if err == nil {
 | 
						|
			ps.historyHandles = append(ps.historyHandles, h)
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
		if terror.ErrorNotEqual(err, kv.ErrKeyExists) {
 | 
						|
			return errors.Trace(err)
 | 
						|
		}
 | 
						|
		// THREAD_ID is PK
 | 
						|
		handle := int64(record[0].GetUint64())
 | 
						|
		err = tbl.UpdateRecord(nil, handle, nil, record, nil)
 | 
						|
		return errors.Trace(err)
 | 
						|
 | 
						|
	}
 | 
						|
	// If histroy is full, replace old data
 | 
						|
	if ps.historyCursor >= len(ps.historyHandles) {
 | 
						|
		ps.historyCursor = 0
 | 
						|
	}
 | 
						|
	h := ps.historyHandles[ps.historyCursor]
 | 
						|
	ps.historyCursor++
 | 
						|
	err := tbl.UpdateRecord(nil, h, nil, record, nil)
 | 
						|
	return errors.Trace(err)
 | 
						|
}
 | 
						|
 | 
						|
func registerStatements() {
 | 
						|
	// Existing instrument names are the same as MySQL 5.7
 | 
						|
	PerfHandle.RegisterStatement("sql", "alter_table", (*ast.AlterTableStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "begin", (*ast.BeginStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "commit", (*ast.CommitStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "create_db", (*ast.CreateDatabaseStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "create_index", (*ast.CreateIndexStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "create_table", (*ast.CreateTableStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "deallocate", (*ast.DeallocateStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "delete", (*ast.DeleteStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "do", (*ast.DoStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "drop_db", (*ast.DropDatabaseStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "drop_table", (*ast.DropTableStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "drop_index", (*ast.DropIndexStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "execute", (*ast.ExecuteStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "explain", (*ast.ExplainStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "insert", (*ast.InsertStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "prepare", (*ast.PrepareStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "rollback", (*ast.RollbackStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "select", (*ast.SelectStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "set", (*ast.SetStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "show", (*ast.ShowStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "truncate", (*ast.TruncateTableStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "union", (*ast.UnionStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "update", (*ast.UpdateStmt)(nil))
 | 
						|
	PerfHandle.RegisterStatement("sql", "use", (*ast.UseStmt)(nil))
 | 
						|
}
 |