github.com/aergoio/aergo@v1.3.1/chain/stat.go (about) 1 package chain 2 3 import ( 4 "encoding/json" 5 "sync" 6 "time" 7 8 "github.com/aergoio/aergo/types" 9 ) 10 11 //go:generate stringer -type=statIndex 12 type statIndex int 13 14 const ( 15 // Warning: Each statIndex contant has a String method, which is 16 // automically generated by 'stringer' with 'go generate' command. For the 17 // detail, check https://blog.golang.org/generate 18 19 // ReorgStat is a constant representing a stat about reorganization. 20 ReorgStat statIndex = iota 21 // MaxStat is a constant representing a value less than which all the 22 // constants corresponding chain stats must be. 23 MaxStat 24 ) 25 26 var ( 27 // To add a new one to chain stats, implements statItem interface and add 28 // its constructor here. Additionally you need to add a constant 29 // corresponding to its index like statReorg above. 30 statItemCtors = map[statIndex]func() statItem{ 31 ReorgStat: newStReorg, 32 } 33 ) 34 35 type stats []*stat 36 37 func newStats() stats { 38 s := make(stats, MaxStat) 39 for i := statIndex(0); i < MaxStat; i++ { 40 s[i] = newStat(statItemCtors[i]()) 41 } 42 return s 43 } 44 45 func (s stats) JSON() string { 46 r := make(map[string]json.RawMessage) 47 for i := statIndex(0); i < MaxStat; i++ { 48 if b, err := json.Marshal(s.clone(i)); err == nil { 49 r[i.String()] = json.RawMessage(b) 50 } 51 } 52 if m, err := json.Marshal(r); err == nil { 53 return string(m) 54 } 55 return "" 56 } 57 58 func (s stats) get(idx statIndex) *stat { 59 return []*stat(s)[idx] 60 } 61 62 func (s stats) clone(idx statIndex) interface{} { 63 i := s.get(idx) 64 i.RLock() 65 defer i.RUnlock() 66 return i.clone() 67 } 68 69 func (s stats) updateEvent(idx statIndex, args ...interface{}) { 70 i := s.get(idx) 71 i.Lock() 72 defer i.Unlock() 73 74 i.updateEvent(args...) 75 } 76 77 type stat struct { 78 sync.RWMutex 79 statItem 80 } 81 82 func newStat(i statItem) *stat { 83 return &stat{statItem: i} 84 } 85 86 type statItem interface { 87 updateEvent(args ...interface{}) 88 clone() interface{} 89 } 90 91 type stReorg struct { 92 totalElapsed time.Duration 93 Count int64 94 AverageElapsed float64 `json:"Average Elapsed Time,omitempty"` 95 Latest *evReorg `json:",omitempty"` 96 } 97 98 func newStReorg() statItem { 99 return &stReorg{} 100 } 101 102 type evReorg struct { 103 OldBest *blockInfo `json:"Old Best,omitempty"` 104 Fork *blockInfo `json:"Fork At,omitempty"` 105 NewBest *blockInfo `json:"New Best,omitempty"` 106 Time time.Time 107 } 108 109 type blockInfo struct { 110 Hash string 111 Height types.BlockNo 112 } 113 114 func (sr *stReorg) getCount() int64 { 115 return sr.Count 116 } 117 118 func (sr *stReorg) getLatestEvent() interface{} { 119 return sr.Latest 120 } 121 122 func (sr *stReorg) updateEvent(args ...interface{}) { 123 if len(args) != 4 { 124 logger.Info().Int("len", len(args)).Msg("invalid # of arguments for the reorg stat update") 125 return 126 } 127 128 et := args[0].(time.Duration) 129 130 bi := make([]*blockInfo, len(args)) 131 for i, a := range args[1:] { 132 var block *types.Block 133 ok := false 134 if block, ok = a.(*types.Block); !ok { 135 logger.Info().Int("arg idx", i).Msg("invalid type of argument") 136 return 137 } 138 bi[i] = &blockInfo{Hash: block.ID(), Height: block.BlockNo()} 139 } 140 141 sr.Latest = &evReorg{ 142 OldBest: bi[0], 143 NewBest: bi[1], 144 Fork: bi[2], 145 Time: time.Now(), 146 } 147 148 sr.totalElapsed += et 149 sr.Count++ 150 sr.AverageElapsed = (sr.totalElapsed / time.Duration(sr.Count)).Seconds() 151 } 152 153 func (sr *stReorg) clone() interface{} { 154 c := *sr 155 if sr.Latest != nil { 156 l := *sr.Latest 157 c.Latest = &l 158 } 159 160 return &c 161 }