github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/p2p/protocols/reporter.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package protocols 18 19 import ( 20 "encoding/binary" 21 "time" 22 23 "github.com/ethereum/go-ethereum/log" 24 "github.com/ethereum/go-ethereum/metrics" 25 26 "github.com/syndtr/goleveldb/leveldb" 27 ) 28 29 //AccountMetrics abstracts away the metrics DB and 30 //the reporter to persist metrics 31 type AccountingMetrics struct { 32 reporter *reporter 33 } 34 35 //Close will be called when the node is being shutdown 36 //for a graceful cleanup 37 func (am *AccountingMetrics) Close() { 38 close(am.reporter.quit) 39 // wait for reporter loop to finish saving metrics 40 // before reporter database is closed 41 select { 42 case <-time.After(10 * time.Second): 43 log.Error("accounting metrics reporter timeout") 44 case <-am.reporter.done: 45 } 46 am.reporter.db.Close() 47 } 48 49 //reporter is an internal structure used to write p2p accounting related 50 //metrics to a LevelDB. It will periodically write the accrued metrics to the DB. 51 type reporter struct { 52 reg metrics.Registry //the registry for these metrics (independent of other metrics) 53 interval time.Duration //duration at which the reporter will persist metrics 54 db *leveldb.DB //the actual DB 55 quit chan struct{} //quit the reporter loop 56 done chan struct{} //signal that reporter loop is done 57 } 58 59 //NewMetricsDB creates a new LevelDB instance used to persist metrics defined 60 //inside p2p/protocols/accounting.go 61 func NewAccountingMetrics(r metrics.Registry, d time.Duration, path string) *AccountingMetrics { 62 var val = make([]byte, 8) 63 var err error 64 65 //Create the LevelDB 66 db, err := leveldb.OpenFile(path, nil) 67 if err != nil { 68 log.Error(err.Error()) 69 return nil 70 } 71 72 //Check for all defined metrics that there is a value in the DB 73 //If there is, assign it to the metric. This means that the node 74 //has been running before and that metrics have been persisted. 75 metricsMap := map[string]metrics.Counter{ 76 "account.balance.credit": mBalanceCredit, 77 "account.balance.debit": mBalanceDebit, 78 "account.bytes.credit": mBytesCredit, 79 "account.bytes.debit": mBytesDebit, 80 "account.msg.credit": mMsgCredit, 81 "account.msg.debit": mMsgDebit, 82 "account.peerdrops": mPeerDrops, 83 "account.selfdrops": mSelfDrops, 84 } 85 //iterate the map and get the values 86 for key, metric := range metricsMap { 87 val, err = db.Get([]byte(key), nil) 88 //until the first time a value is being written, 89 //this will return an error. 90 //it could be beneficial though to log errors later, 91 //but that would require a different logic 92 if err == nil { 93 metric.Inc(int64(binary.BigEndian.Uint64(val))) 94 } 95 } 96 97 //create the reporter 98 rep := &reporter{ 99 reg: r, 100 interval: d, 101 db: db, 102 quit: make(chan struct{}), 103 done: make(chan struct{}), 104 } 105 106 //run the go routine 107 go rep.run() 108 109 m := &AccountingMetrics{ 110 reporter: rep, 111 } 112 113 return m 114 } 115 116 //run is the goroutine which periodically sends the metrics to the configured LevelDB 117 func (r *reporter) run() { 118 // signal that the reporter loop is done 119 defer close(r.done) 120 121 intervalTicker := time.NewTicker(r.interval) 122 123 for { 124 select { 125 case <-intervalTicker.C: 126 //at each tick send the metrics 127 if err := r.save(); err != nil { 128 log.Error("unable to send metrics to LevelDB", "err", err) 129 //If there is an error in writing, exit the routine; we assume here that the error is 130 //severe and don't attempt to write again. 131 //Also, this should prevent leaking when the node is stopped 132 return 133 } 134 case <-r.quit: 135 //graceful shutdown 136 if err := r.save(); err != nil { 137 log.Error("unable to send metrics to LevelDB", "err", err) 138 } 139 return 140 } 141 } 142 } 143 144 //send the metrics to the DB 145 func (r *reporter) save() error { 146 //create a LevelDB Batch 147 batch := leveldb.Batch{} 148 //for each metric in the registry (which is independent)... 149 r.reg.Each(func(name string, i interface{}) { 150 metric, ok := i.(metrics.Counter) 151 if ok { 152 //assuming every metric here to be a Counter (separate registry) 153 //...create a snapshot... 154 ms := metric.Snapshot() 155 byteVal := make([]byte, 8) 156 binary.BigEndian.PutUint64(byteVal, uint64(ms.Count())) 157 //...and save the value to the DB 158 batch.Put([]byte(name), byteVal) 159 } 160 }) 161 return r.db.Write(&batch, nil) 162 }