github.com/matrixorigin/matrixone@v1.2.0/pkg/util/trace/impl/motrace/statistic/stats_array.go (about)

     1  // Copyright 2021 - 2023 Matrix Origin
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package statistic
    16  
    17  import (
    18  	"context"
    19  	"strconv"
    20  	"sync/atomic"
    21  	"time"
    22  )
    23  
    24  type StatsArray [StatsArrayLength]float64
    25  
    26  const (
    27  	Decimal128ToFloat64Scale = 5
    28  	Float64PrecForMemorySize = 3
    29  	Float64PrecForCU         = 4
    30  )
    31  
    32  const StatsArrayVersion = StatsArrayVersionLatest
    33  
    34  const (
    35  	StatsArrayVersion0 = iota // raw statistics
    36  
    37  	StatsArrayVersion1 = 1 // float64 array
    38  	StatsArrayVersion2 = 2 // float64 array + plus one elem OutTrafficBytes
    39  	StatsArrayVersion3 = 3 // ... + 1 elem: ConnType
    40  	StatsArrayVersion4 = 4 // ... + 2 elem: OutPacketCount, CU
    41  
    42  	StatsArrayVersionLatest // same value as last variable StatsArrayVersion#
    43  )
    44  
    45  const (
    46  	StatsArrayIndexVersion = iota
    47  	StatsArrayIndexTimeConsumed
    48  	StatsArrayIndexMemorySize
    49  	StatsArrayIndexS3IOInputCount
    50  	StatsArrayIndexS3IOOutputCount // index: 4
    51  	StatsArrayIndexOutTrafficBytes // index: 5
    52  	StatsArrayIndexConnType        // index: 6
    53  	StatsArrayIndexOutPacketCnt    // index: 7, version: 4
    54  	StatsArrayIndexCU              // index: 8, version: 4
    55  
    56  	StatsArrayLength
    57  )
    58  
    59  const (
    60  	StatsArrayLengthV1 = 5
    61  	StatsArrayLengthV2 = 6
    62  	StatsArrayLengthV3 = 7
    63  	StatsArrayLengthV4 = 9
    64  )
    65  
    66  type ConnType float64
    67  
    68  const (
    69  	ConnTypeUnknown  ConnType = 0
    70  	ConnTypeInternal ConnType = 1
    71  	ConnTypeExternal ConnType = 2
    72  )
    73  
    74  func NewStatsArray() *StatsArray {
    75  	var s StatsArray
    76  	return s.Init()
    77  }
    78  
    79  func NewStatsArrayV1() *StatsArray {
    80  	return NewStatsArray().WithVersion(StatsArrayVersion1)
    81  }
    82  
    83  func NewStatsArrayV2() *StatsArray {
    84  	return NewStatsArray().WithVersion(StatsArrayVersion2)
    85  }
    86  
    87  func NewStatsArrayV3() *StatsArray {
    88  	return NewStatsArray().WithVersion(StatsArrayVersion3)
    89  }
    90  
    91  func NewStatsArrayV4() *StatsArray {
    92  	return NewStatsArray().WithVersion(StatsArrayVersion4)
    93  }
    94  
    95  func (s *StatsArray) Init() *StatsArray {
    96  	return s.WithVersion(StatsArrayVersion)
    97  }
    98  
    99  func (s *StatsArray) InitIfEmpty() *StatsArray {
   100  	for i := 1; i < StatsArrayLength; i++ {
   101  		if s[i] != 0 {
   102  			return s
   103  		}
   104  	}
   105  	return s.WithVersion(StatsArrayVersion)
   106  }
   107  
   108  func (s *StatsArray) Reset() *StatsArray {
   109  	*s = *initStatsArray
   110  	return s
   111  }
   112  
   113  func (s *StatsArray) GetVersion() float64         { return (*s)[StatsArrayIndexVersion] }
   114  func (s *StatsArray) GetTimeConsumed() float64    { return (*s)[StatsArrayIndexTimeConsumed] }    // unit: ns
   115  func (s *StatsArray) GetMemorySize() float64      { return (*s)[StatsArrayIndexMemorySize] }      // unit: byte
   116  func (s *StatsArray) GetS3IOInputCount() float64  { return (*s)[StatsArrayIndexS3IOInputCount] }  // unit: count
   117  func (s *StatsArray) GetS3IOOutputCount() float64 { return (*s)[StatsArrayIndexS3IOOutputCount] } // unit: count
   118  func (s *StatsArray) GetOutTrafficBytes() float64 { // unit: byte
   119  	if s.GetVersion() < StatsArrayVersion2 {
   120  		return 0
   121  	}
   122  	return (*s)[StatsArrayIndexOutTrafficBytes]
   123  }
   124  func (s *StatsArray) GetConnType() float64 {
   125  	if s.GetVersion() < StatsArrayVersion3 {
   126  		return 0
   127  	}
   128  	return (*s)[StatsArrayIndexConnType]
   129  }
   130  func (s *StatsArray) GetOutPacketCount() float64 {
   131  	if s.GetVersion() < StatsArrayVersion4 {
   132  		return 0
   133  	}
   134  	return s[StatsArrayIndexOutPacketCnt]
   135  }
   136  func (s *StatsArray) GetCU() float64 {
   137  	if s.GetVersion() < StatsArrayVersion4 {
   138  		return 0
   139  	}
   140  	return s[StatsArrayIndexCU]
   141  }
   142  
   143  // WithVersion set the version array in StatsArray, please carefully to use.
   144  func (s *StatsArray) WithVersion(v float64) *StatsArray { (*s)[StatsArrayIndexVersion] = v; return s }
   145  func (s *StatsArray) WithTimeConsumed(v float64) *StatsArray {
   146  	(*s)[StatsArrayIndexTimeConsumed] = v
   147  	return s
   148  }
   149  func (s *StatsArray) WithMemorySize(v float64) *StatsArray {
   150  	(*s)[StatsArrayIndexMemorySize] = v
   151  	return s
   152  }
   153  func (s *StatsArray) WithS3IOInputCount(v float64) *StatsArray {
   154  	(*s)[StatsArrayIndexS3IOInputCount] = v
   155  	return s
   156  }
   157  func (s *StatsArray) WithS3IOOutputCount(v float64) *StatsArray {
   158  	(*s)[StatsArrayIndexS3IOOutputCount] = v
   159  	return s
   160  }
   161  func (s *StatsArray) WithOutTrafficBytes(v float64) *StatsArray {
   162  	if s.GetVersion() >= StatsArrayVersion2 {
   163  		(*s)[StatsArrayIndexOutTrafficBytes] = v
   164  	}
   165  	return s
   166  }
   167  
   168  func (s *StatsArray) WithConnType(v ConnType) *StatsArray {
   169  	if s.GetVersion() >= StatsArrayVersion3 {
   170  		(*s)[StatsArrayIndexConnType] = float64(v)
   171  	}
   172  	return s
   173  }
   174  
   175  func (s *StatsArray) WithOutPacketCount(v float64) *StatsArray {
   176  	s[StatsArrayIndexOutPacketCnt] = v
   177  	return s
   178  }
   179  
   180  func (s *StatsArray) WithCU(v float64) *StatsArray {
   181  	s[StatsArrayIndexCU] = v
   182  	return s
   183  }
   184  
   185  func (s *StatsArray) ToJsonString() []byte {
   186  	switch s.GetVersion() {
   187  	case StatsArrayVersion1:
   188  		return StatsArrayToJsonString((*s)[:StatsArrayLengthV1])
   189  	case StatsArrayVersion2:
   190  		return StatsArrayToJsonString((*s)[:StatsArrayLengthV2])
   191  	case StatsArrayVersion3:
   192  		return StatsArrayToJsonString((*s)[:StatsArrayLengthV3])
   193  	case StatsArrayVersion4:
   194  		return StatsArrayToJsonString((*s)[:StatsArrayLengthV4])
   195  	default:
   196  		return StatsArrayToJsonString((*s)[:])
   197  	}
   198  }
   199  
   200  // Add do add two stats array together
   201  // except for Element ConnType, which idx = StatsArrayIndexConnType, just keep s[StatsArrayIndexConnType] value.
   202  func (s *StatsArray) Add(delta *StatsArray) *StatsArray {
   203  	dstLen := len(*delta)
   204  	if len(*s) < len(*delta) {
   205  		dstLen = len(*s)
   206  	}
   207  	for idx := 1; idx < dstLen; idx++ {
   208  		if idx == StatsArrayIndexConnType {
   209  			continue
   210  		}
   211  		(*s)[idx] += (*delta)[idx]
   212  	}
   213  	return s
   214  }
   215  
   216  // StatsArrayToJsonString return json arr format
   217  // example:
   218  // [1,0,0,0,0] got `[1,0,0,0,0]`
   219  // [1,2,3,4,5] got `[1,2,3.000,4,5]`
   220  // [2,1,2,3,4,5] got `[2,3.000,4,5,6.000,7]`
   221  func StatsArrayToJsonString(arr []float64) []byte {
   222  	// len([1,184467440737095516161,18446744073709551616,18446744073709551616,18446744073709551616]") = 88
   223  	buf := make([]byte, 0, 128)
   224  	buf = append(buf, '[')
   225  	for idx, v := range arr {
   226  		if idx > 0 {
   227  			buf = append(buf, ',')
   228  		}
   229  		if v == 0.0 {
   230  			buf = append(buf, '0')
   231  		} else if idx == StatsArrayIndexMemorySize {
   232  			buf = strconv.AppendFloat(buf, v, 'f', Float64PrecForMemorySize, 64)
   233  		} else if idx == StatsArrayIndexCU {
   234  			buf = strconv.AppendFloat(buf, v, 'f', Float64PrecForCU, 64)
   235  		} else {
   236  			buf = strconv.AppendFloat(buf, v, 'f', 0, 64)
   237  		}
   238  	}
   239  	buf = append(buf, ']')
   240  	return buf
   241  }
   242  
   243  var initStatsArray = NewStatsArray()
   244  
   245  var DefaultStatsArray = *initStatsArray
   246  
   247  var DefaultStatsArrayJsonString = initStatsArray.ToJsonString()
   248  
   249  type statsInfoKey struct{}
   250  
   251  // statistic info of sql
   252  type StatsInfo struct {
   253  	ParseDuration     time.Duration `json:"ParseDuration"`
   254  	PlanDuration      time.Duration `json:"PlanDuration"`
   255  	CompileDuration   time.Duration `json:"CompileDuration"`
   256  	ExecutionDuration time.Duration `json:"ExecutionDuration"`
   257  
   258  	//PipelineTimeConsumption      time.Duration
   259  	//PipelineBlockTimeConsumption time.Duration
   260  
   261  	IOAccessTimeConsumption int64
   262  	//S3ReadBytes             uint
   263  	//S3WriteBytes            uint
   264  
   265  	LocalFSReadIOMergerTimeConsumption      int64
   266  	LocalFSReadCacheIOMergerTimeConsumption int64
   267  
   268  	S3FSPrefetchFileIOMergerTimeConsumption int64
   269  	S3FSReadIOMergerTimeConsumption         int64
   270  	S3FSReadCacheIOMergerTimeConsumption    int64
   271  
   272  	ParseStartTime     time.Time `json:"ParseStartTime"`
   273  	PlanStartTime      time.Time `json:"PlanStartTime"`
   274  	CompileStartTime   time.Time `json:"CompileStartTime"`
   275  	ExecutionStartTime time.Time `json:"ExecutionStartTime"`
   276  	ExecutionEndTime   time.Time `json:"ExecutionEndTime"`
   277  
   278  	WaitActiveCost time.Duration `json:"WaitActive"`
   279  }
   280  
   281  func (stats *StatsInfo) CompileStart() {
   282  	if stats == nil {
   283  		return
   284  	}
   285  	if !stats.CompileStartTime.IsZero() {
   286  		return
   287  	}
   288  	stats.CompileStartTime = time.Now()
   289  }
   290  
   291  func (stats *StatsInfo) CompileEnd() {
   292  	if stats == nil {
   293  		return
   294  	}
   295  	stats.CompileDuration = time.Since(stats.CompileStartTime)
   296  }
   297  
   298  func (stats *StatsInfo) PlanStart() {
   299  	if stats == nil {
   300  		return
   301  	}
   302  	stats.PlanStartTime = time.Now()
   303  }
   304  
   305  func (stats *StatsInfo) PlanEnd() {
   306  	if stats == nil {
   307  		return
   308  	}
   309  	stats.PlanDuration = time.Since(stats.PlanStartTime)
   310  }
   311  
   312  func (stats *StatsInfo) ExecutionStart() {
   313  	if stats == nil {
   314  		return
   315  	}
   316  	stats.ExecutionStartTime = time.Now()
   317  }
   318  
   319  func (stats *StatsInfo) ExecutionEnd() {
   320  	if stats == nil {
   321  		return
   322  	}
   323  	stats.ExecutionEndTime = time.Now()
   324  	stats.ExecutionDuration = stats.ExecutionEndTime.Sub(stats.ExecutionStartTime)
   325  }
   326  
   327  func (stats *StatsInfo) AddIOAccessTimeConsumption(d time.Duration) {
   328  	if stats == nil {
   329  		return
   330  	}
   331  	atomic.AddInt64(&stats.IOAccessTimeConsumption, int64(d))
   332  }
   333  
   334  func (stats *StatsInfo) AddLocalFSReadCacheIOMergerTimeConsumption(d time.Duration) {
   335  	if stats == nil {
   336  		return
   337  	}
   338  	atomic.AddInt64(&stats.LocalFSReadCacheIOMergerTimeConsumption, int64(d))
   339  }
   340  func (stats *StatsInfo) AddLocalFSReadIOMergerTimeConsumption(d time.Duration) {
   341  	if stats == nil {
   342  		return
   343  	}
   344  	atomic.AddInt64(&stats.LocalFSReadIOMergerTimeConsumption, int64(d))
   345  }
   346  func (stats *StatsInfo) AddS3FSPrefetchFileIOMergerTimeConsumption(d time.Duration) {
   347  	if stats == nil {
   348  		return
   349  	}
   350  	atomic.AddInt64(&stats.S3FSPrefetchFileIOMergerTimeConsumption, int64(d))
   351  }
   352  func (stats *StatsInfo) AddS3FSReadIOMergerTimeConsumption(d time.Duration) {
   353  	if stats == nil {
   354  		return
   355  	}
   356  	atomic.AddInt64(&stats.S3FSReadIOMergerTimeConsumption, int64(d))
   357  }
   358  func (stats *StatsInfo) AddS3FSReadCacheIOMergerTimeConsumption(d time.Duration) {
   359  	if stats == nil {
   360  		return
   361  	}
   362  	atomic.AddInt64(&stats.S3FSReadCacheIOMergerTimeConsumption, int64(d))
   363  }
   364  
   365  func (stats *StatsInfo) IOMergerTimeConsumption() int64 {
   366  	if stats == nil {
   367  		return 0
   368  	}
   369  	return stats.LocalFSReadCacheIOMergerTimeConsumption +
   370  		stats.LocalFSReadIOMergerTimeConsumption +
   371  		stats.S3FSPrefetchFileIOMergerTimeConsumption +
   372  		stats.S3FSReadCacheIOMergerTimeConsumption +
   373  		stats.S3FSReadIOMergerTimeConsumption
   374  }
   375  
   376  func (stats *StatsInfo) SetWaitActiveCost(cost time.Duration) {
   377  	if stats == nil {
   378  		return
   379  	}
   380  	stats.WaitActiveCost = cost
   381  }
   382  
   383  // reset StatsInfo into zero state
   384  func (stats *StatsInfo) Reset() {
   385  	if stats == nil {
   386  		return
   387  	}
   388  	*stats = StatsInfo{}
   389  }
   390  
   391  func ContextWithStatsInfo(requestCtx context.Context, stats *StatsInfo) context.Context {
   392  	return context.WithValue(requestCtx, statsInfoKey{}, stats)
   393  }
   394  
   395  func StatsInfoFromContext(requestCtx context.Context) *StatsInfo {
   396  	if requestCtx == nil {
   397  		return nil
   398  	}
   399  	if stats, ok := requestCtx.Value(statsInfoKey{}).(*StatsInfo); ok {
   400  		return stats
   401  	}
   402  	return nil
   403  }
   404  
   405  // EnsureStatsInfoCanBeFound ensure a statement statistic is set in context, if not, copy one from another context, this function is copied from EnsureStatementProfiler
   406  func EnsureStatsInfoCanBeFound(ctx context.Context, from context.Context) context.Context {
   407  	if v := ctx.Value(statsInfoKey{}); v != nil {
   408  		// already set
   409  		return ctx
   410  	}
   411  	v := from.Value(statsInfoKey{})
   412  	if v == nil {
   413  		// not set in from
   414  		return ctx
   415  	}
   416  	ctx = context.WithValue(ctx, statsInfoKey{}, v)
   417  	return ctx
   418  }