go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/stats.go (about)

     1  //  Copyright (c) 2019 Cisco and/or its affiliates.
     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 kvscheduler
    16  
    17  import (
    18  	"encoding/json"
    19  	"expvar"
    20  	"sync"
    21  	"time"
    22  
    23  	"go.ligato.io/vpp-agent/v3/pkg/metrics"
    24  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    25  	"go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler"
    26  )
    27  
    28  var (
    29  	stats   Stats
    30  	statsMu sync.RWMutex
    31  )
    32  
    33  func init() {
    34  	stats.GraphMethods.Methods = make(metrics.Calls)
    35  	stats.AllDescriptors.Methods = make(metrics.Calls)
    36  	stats.Descriptors = make(map[string]*StructStats)
    37  	stats.TxnStats.Methods = make(metrics.Calls)
    38  	stats.TxnStats.OperationCount = make(map[string]uint64)
    39  	stats.TxnStats.ValueStateCount = make(map[string]uint64)
    40  	for state := range kvscheduler.ValueState_value {
    41  		stats.TxnStats.ValueStateCount[state] = 0
    42  	}
    43  	for op, opVal := range kvscheduler.TxnOperation_name {
    44  		if op == int32(kvscheduler.TxnOperation_UNDEFINED) ||
    45  			op == int32(kvscheduler.TxnOperation_VALIDATE) {
    46  			continue
    47  		}
    48  		stats.TxnStats.OperationCount[opVal] = 0
    49  	}
    50  }
    51  
    52  func GetStats() *Stats {
    53  	s := new(Stats)
    54  	statsMu.RLock()
    55  	*s = stats
    56  	statsMu.RUnlock()
    57  	return s
    58  }
    59  
    60  type Stats struct {
    61  	TxnStats       TxnStats
    62  	GraphMethods   StructStats
    63  	AllDescriptors StructStats
    64  	Descriptors    map[string]*StructStats
    65  }
    66  
    67  func (s *Stats) addDescriptor(name string) {
    68  	s.Descriptors[name] = &StructStats{
    69  		Methods: make(metrics.Calls),
    70  	}
    71  }
    72  
    73  type TxnStats struct {
    74  	TotalProcessed  uint64
    75  	OperationCount  map[string]uint64
    76  	ValueStateCount map[string]uint64
    77  	ErrorCount      uint64
    78  	Methods         metrics.Calls
    79  }
    80  
    81  type StructStats struct {
    82  	Methods metrics.Calls `json:"-,omitempty"`
    83  }
    84  
    85  func (s *StructStats) MarshalJSON() ([]byte, error) {
    86  	return json.Marshal(s.Methods)
    87  }
    88  
    89  func (s *StructStats) getOrCreateMethod(method string) *metrics.CallStats {
    90  	statsMu.RLock()
    91  	ms, ok := s.Methods[method]
    92  	statsMu.RUnlock()
    93  	if !ok {
    94  		ms = &metrics.CallStats{Name: method}
    95  		statsMu.Lock()
    96  		s.Methods[method] = ms
    97  		statsMu.Unlock()
    98  	}
    99  	return ms
   100  }
   101  
   102  func trackDescMethod(d, m string) func() {
   103  	t := time.Now()
   104  	method := stats.Descriptors[d].getOrCreateMethod(m)
   105  	methodall := stats.AllDescriptors.getOrCreateMethod(m)
   106  	return func() {
   107  		took := time.Since(t)
   108  		statsMu.Lock()
   109  		method.Increment(took)
   110  		methodall.Increment(took)
   111  		statsMu.Unlock()
   112  	}
   113  }
   114  
   115  func trackGraphMethod(m string) func() {
   116  	t := time.Now()
   117  	method := stats.GraphMethods.getOrCreateMethod(m)
   118  	return func() {
   119  		took := time.Since(t)
   120  		statsMu.Lock()
   121  		method.Increment(took)
   122  		statsMu.Unlock()
   123  	}
   124  }
   125  
   126  func trackTransactionMethod(m string) func() {
   127  	t := time.Now()
   128  	s := stats.TxnStats
   129  	return func() {
   130  		took := time.Since(t)
   131  		reportTxnProcessDuration(m, took.Seconds())
   132  		statsMu.Lock()
   133  		ms, tracked := s.Methods[m]
   134  		if !tracked {
   135  			ms = &metrics.CallStats{Name: m}
   136  			s.Methods[m] = ms
   137  		}
   138  		ms.Increment(took)
   139  		statsMu.Unlock()
   140  	}
   141  }
   142  
   143  func updateTransactionStats(execOps kvs.RecordedTxnOps) {
   144  	statsMu.Lock()
   145  	defer statsMu.Unlock()
   146  	stats.TxnStats.TotalProcessed++
   147  	for _, op := range execOps {
   148  		if op.NewErr != nil {
   149  			stats.TxnStats.ErrorCount++
   150  		}
   151  		stats.TxnStats.OperationCount[op.Operation.String()]++
   152  		stats.TxnStats.ValueStateCount[op.NewState.String()]++
   153  	}
   154  }
   155  
   156  func init() {
   157  	expvar.Publish("kvscheduler", expvar.Func(func() interface{} {
   158  		return GetStats()
   159  	}))
   160  }