github.com/aswedchain/aswed@v1.0.1/core/tx_jam_indexer.go (about) 1 package core 2 3 import ( 4 "math/big" 5 "sort" 6 "sync" 7 "time" 8 9 "github.com/aswedchain/aswed/core/types" 10 "github.com/aswedchain/aswed/log" 11 "github.com/aswedchain/aswed/metrics" 12 ) 13 14 var ( 15 jamIndexMeter = metrics.NewRegisteredMeter("txpool/jamindex", nil) 16 ) 17 18 var oneGwei = big.NewInt(1e9) 19 20 var DefaultJamConfig = TxJamConfig{ 21 PeriodsSecs: 3, 22 JamSecs: 15, 23 UnderPricedFactor: 3, 24 PendingFactor: 1, 25 MaxValidPendingSecs: 300, 26 } 27 28 type TxJamConfig struct { 29 PeriodsSecs int // how many seconds to do a reflesh to the jam index 30 JamSecs int // how many seconds for a tx pending that will meams a tx-jam. 31 UnderPricedFactor int 32 PendingFactor int 33 34 MaxValidPendingSecs int // 35 } 36 37 func (c *TxJamConfig) sanity() TxJamConfig { 38 cfg := *c 39 if cfg.PeriodsSecs < 1 { 40 log.Info("JamConfig sanity PeriodsSecs", "old", cfg.PeriodsSecs, "new", DefaultJamConfig.PeriodsSecs) 41 cfg.PeriodsSecs = DefaultJamConfig.PeriodsSecs 42 } 43 if cfg.JamSecs < 3 { 44 log.Info("JamConfig sanity JamSecs", "old", cfg.JamSecs, "new", DefaultJamConfig.JamSecs) 45 cfg.JamSecs = DefaultJamConfig.JamSecs 46 } 47 if cfg.UnderPricedFactor < 1 { 48 log.Info("JamConfig sanity UnderPricedFactor", "old", cfg.UnderPricedFactor, "new", DefaultJamConfig.UnderPricedFactor) 49 cfg.UnderPricedFactor = DefaultJamConfig.UnderPricedFactor 50 } 51 if cfg.PendingFactor < 1 { 52 log.Info("JamConfig sanity PendingFactor", "old", cfg.PendingFactor, "new", DefaultJamConfig.PendingFactor) 53 cfg.PendingFactor = DefaultJamConfig.PendingFactor 54 } 55 if cfg.MaxValidPendingSecs <= cfg.JamSecs { 56 log.Info("JamConfig sanity MaxValidPendingSecs", "old", cfg.MaxValidPendingSecs, "new", DefaultJamConfig.MaxValidPendingSecs) 57 cfg.MaxValidPendingSecs = DefaultJamConfig.MaxValidPendingSecs 58 } 59 return cfg 60 } 61 62 // txJamIndexer try to give a quantitative index to reflects the tx-jam. 63 type txJamIndexer struct { 64 cfg TxJamConfig 65 pool *TxPool 66 head *types.Header 67 68 undCounter *underPricedCounter 69 currentJamIndex int 70 71 pendingLock sync.Mutex 72 jamLock sync.RWMutex 73 74 quit chan struct{} 75 chainHeadCh chan *types.Header 76 } 77 78 func newTxJamIndexer(cfg TxJamConfig, pool *TxPool) *txJamIndexer { 79 cfg = (&cfg).sanity() 80 81 indexer := &txJamIndexer{ 82 cfg: cfg, 83 pool: pool, 84 undCounter: newUnderPricedCounter(cfg.PeriodsSecs), 85 quit: make(chan struct{}), 86 chainHeadCh: make(chan *types.Header, 1), 87 } 88 89 go indexer.updateLoop() 90 91 return indexer 92 } 93 94 // Stop stops the loop goroutines of this TxJamIndexer 95 func (indexer *txJamIndexer) Stop() { 96 indexer.undCounter.Stop() 97 close(indexer.quit) 98 } 99 100 // JamIndex returns the current jam index 101 func (indexer *txJamIndexer) JamIndex() int { 102 indexer.jamLock.RLock() 103 defer indexer.jamLock.RUnlock() 104 return indexer.currentJamIndex 105 } 106 107 func (indexer *txJamIndexer) updateLoop() { 108 tick := time.NewTicker(time.Second * time.Duration(indexer.cfg.PeriodsSecs)) 109 defer tick.Stop() 110 111 for { 112 select { 113 case h := <-indexer.chainHeadCh: 114 indexer.head = h 115 case <-tick.C: 116 d := indexer.undCounter.Sum() 117 pendings, _ := indexer.pool.Pending() 118 if d == 0 && len(pendings) == 0 { 119 break 120 } 121 // flatten 122 var p int 123 max := indexer.cfg.MaxValidPendingSecs 124 jamsecs := indexer.cfg.JamSecs 125 maxGas := uint64(10000000) 126 if indexer.head != nil { 127 maxGas = (indexer.head.GasLimit / 10) * 6 128 } 129 durs := make([]time.Duration, 0, 1024) 130 for _, txs := range pendings { 131 for _, tx := range txs { 132 // filtering 133 if tx.GasPrice().Cmp(oneGwei) < 0 || 134 tx.Gas() > maxGas { 135 continue 136 } 137 138 dur := time.Since(tx.LocalSeenTime()) 139 sec := int(dur / time.Second) 140 if sec > max { 141 continue 142 } 143 144 durs = append(durs, dur) 145 if sec >= jamsecs { 146 p += sec / jamsecs 147 } 148 } 149 } 150 nTotal := len(durs) 151 152 if nTotal == 0 { 153 p = 0 154 } else { 155 p = 100 * p / nTotal 156 } 157 158 idx := d*indexer.cfg.UnderPricedFactor + p*indexer.cfg.PendingFactor 159 indexer.jamLock.Lock() 160 indexer.currentJamIndex = idx 161 indexer.jamLock.Unlock() 162 jamIndexMeter.Mark(int64(idx)) 163 164 var dists []time.Duration 165 sort.Slice(durs, func(i, j int) bool { 166 return durs[i] < durs[j] 167 }) 168 if nTotal > 10 { 169 dists = append(dists, durs[0]) 170 for i := 1; i < 10; i++ { 171 dists = append(dists, durs[nTotal*i/10]) 172 } 173 dists = append(dists, durs[nTotal-1]) 174 } else { 175 dists = durs 176 } 177 178 log.Trace("TxJamIndexer", "jamIndex", idx, "d", d, "p", p, "n", nTotal, "dists", dists) 179 case <-indexer.quit: 180 return 181 } 182 } 183 } 184 185 func (indexer *txJamIndexer) UpdateHeader(h *types.Header) { 186 indexer.chainHeadCh <- h 187 } 188 189 func (indexer *txJamIndexer) UnderPricedInc() { 190 indexer.undCounter.Inc() 191 } 192 193 type underPricedCounter struct { 194 counts []int // the lenght of this slice is 2 times of periodSecs 195 periods int //how many periods to cache, each period cache records of 0.5 seconds. 196 idx int //current index 197 sum int //current sum 198 199 inCh chan struct{} 200 quit chan struct{} 201 queryCh chan struct{} 202 queryResCh chan int 203 } 204 205 func newUnderPricedCounter(periodSecs int) *underPricedCounter { 206 c := &underPricedCounter{ 207 counts: make([]int, 2*periodSecs), 208 periods: 2 * periodSecs, 209 inCh: make(chan struct{}, 10), 210 quit: make(chan struct{}), 211 queryCh: make(chan struct{}), 212 queryResCh: make(chan int), 213 } 214 go c.loop() 215 return c 216 } 217 218 func (c *underPricedCounter) loop() { 219 tick := time.NewTicker(500 * time.Millisecond) 220 defer tick.Stop() 221 222 for { 223 select { 224 case <-tick.C: 225 c.idx = (c.idx + 1) % c.periods 226 c.sum -= c.counts[c.idx] 227 c.counts[c.idx] = 0 228 case <-c.inCh: 229 c.counts[c.idx]++ 230 c.sum++ 231 case <-c.queryCh: 232 c.queryResCh <- c.sum 233 case <-c.quit: 234 return 235 } 236 } 237 } 238 239 func (c *underPricedCounter) Sum() int { 240 var sum int 241 c.queryCh <- struct{}{} 242 sum = <-c.queryResCh 243 return sum 244 } 245 246 func (c *underPricedCounter) Inc() { 247 c.inCh <- struct{}{} 248 } 249 250 func (c *underPricedCounter) Stop() { 251 close(c.quit) 252 }