github.com/reapchain/go-reapchain@v0.2.15-0.20210609012950-9735c110c705/les/distributor.go (about) 1 // Copyright 2017 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 light implements on-demand retrieval capable state and chain objects 18 // for the Ethereum Light Client. 19 package les 20 21 import ( 22 "container/list" 23 "errors" 24 "sync" 25 "time" 26 ) 27 28 // ErrNoPeers is returned if no peers capable of serving a queued request are available 29 var ErrNoPeers = errors.New("no suitable peers available") 30 31 // requestDistributor implements a mechanism that distributes requests to 32 // suitable peers, obeying flow control rules and prioritizing them in creation 33 // order (even when a resend is necessary). 34 type requestDistributor struct { 35 reqQueue *list.List 36 lastReqOrder uint64 37 stopChn, loopChn chan struct{} 38 loopNextSent bool 39 lock sync.Mutex 40 41 getAllPeers func() map[distPeer]struct{} 42 } 43 44 // distPeer is an LES server peer interface for the request distributor. 45 // waitBefore returns either the necessary waiting time before sending a request 46 // with the given upper estimated cost or the estimated remaining relative buffer 47 // value after sending such a request (in which case the request can be sent 48 // immediately). At least one of these values is always zero. 49 type distPeer interface { 50 waitBefore(uint64) (time.Duration, float64) 51 canQueue() bool 52 queueSend(f func()) 53 } 54 55 // distReq is the request abstraction used by the distributor. It is based on 56 // three callback functions: 57 // - getCost returns the upper estimate of the cost of sending the request to a given peer 58 // - canSend tells if the server peer is suitable to serve the request 59 // - request prepares sending the request to the given peer and returns a function that 60 // does the actual sending. Request order should be preserved but the callback itself should not 61 // block until it is sent because other peers might still be able to receive requests while 62 // one of them is blocking. Instead, the returned function is put in the peer's send queue. 63 type distReq struct { 64 getCost func(distPeer) uint64 65 canSend func(distPeer) bool 66 request func(distPeer) func() 67 68 reqOrder uint64 69 sentChn chan distPeer 70 element *list.Element 71 } 72 73 // newRequestDistributor creates a new request distributor 74 func newRequestDistributor(getAllPeers func() map[distPeer]struct{}, stopChn chan struct{}) *requestDistributor { 75 r := &requestDistributor{ 76 reqQueue: list.New(), 77 loopChn: make(chan struct{}, 2), 78 stopChn: stopChn, 79 getAllPeers: getAllPeers, 80 } 81 go r.loop() 82 return r 83 } 84 85 // distMaxWait is the maximum waiting time after which further necessary waiting 86 // times are recalculated based on new feedback from the servers 87 const distMaxWait = time.Millisecond * 10 88 89 // main event loop 90 func (d *requestDistributor) loop() { 91 for { 92 select { 93 case <-d.stopChn: 94 d.lock.Lock() 95 elem := d.reqQueue.Front() 96 for elem != nil { 97 close(elem.Value.(*distReq).sentChn) 98 elem = elem.Next() 99 } 100 d.lock.Unlock() 101 return 102 case <-d.loopChn: 103 d.lock.Lock() 104 d.loopNextSent = false 105 loop: 106 for { 107 peer, req, wait := d.nextRequest() 108 if req != nil && wait == 0 { 109 chn := req.sentChn // save sentChn because remove sets it to nil 110 d.remove(req) 111 send := req.request(peer) 112 if send != nil { 113 peer.queueSend(send) 114 } 115 chn <- peer 116 close(chn) 117 } else { 118 if wait == 0 { 119 // no request to send and nothing to wait for; the next 120 // queued request will wake up the loop 121 break loop 122 } 123 d.loopNextSent = true // a "next" signal has been sent, do not send another one until this one has been received 124 if wait > distMaxWait { 125 // waiting times may be reduced by incoming request replies, if it is too long, recalculate it periodically 126 wait = distMaxWait 127 } 128 go func() { 129 time.Sleep(wait) 130 d.loopChn <- struct{}{} 131 }() 132 break loop 133 } 134 } 135 d.lock.Unlock() 136 } 137 } 138 } 139 140 // selectPeerItem represents a peer to be selected for a request by weightedRandomSelect 141 type selectPeerItem struct { 142 peer distPeer 143 req *distReq 144 weight int64 145 } 146 147 // Weight implements wrsItem interface 148 func (sp selectPeerItem) Weight() int64 { 149 return sp.weight 150 } 151 152 // nextRequest returns the next possible request from any peer, along with the 153 // associated peer and necessary waiting time 154 func (d *requestDistributor) nextRequest() (distPeer, *distReq, time.Duration) { 155 peers := d.getAllPeers() 156 157 elem := d.reqQueue.Front() 158 var ( 159 bestPeer distPeer 160 bestReq *distReq 161 bestWait time.Duration 162 sel *weightedRandomSelect 163 ) 164 165 for (len(peers) > 0 || elem == d.reqQueue.Front()) && elem != nil { 166 req := elem.Value.(*distReq) 167 canSend := false 168 for peer, _ := range peers { 169 if peer.canQueue() && req.canSend(peer) { 170 canSend = true 171 cost := req.getCost(peer) 172 wait, bufRemain := peer.waitBefore(cost) 173 if wait == 0 { 174 if sel == nil { 175 sel = newWeightedRandomSelect() 176 } 177 sel.update(selectPeerItem{peer: peer, req: req, weight: int64(bufRemain*1000000) + 1}) 178 } else { 179 if bestReq == nil || wait < bestWait { 180 bestPeer = peer 181 bestReq = req 182 bestWait = wait 183 } 184 } 185 delete(peers, peer) 186 } 187 } 188 next := elem.Next() 189 if !canSend && elem == d.reqQueue.Front() { 190 close(req.sentChn) 191 d.remove(req) 192 } 193 elem = next 194 } 195 196 if sel != nil { 197 c := sel.choose().(selectPeerItem) 198 return c.peer, c.req, 0 199 } 200 return bestPeer, bestReq, bestWait 201 } 202 203 // queue adds a request to the distribution queue, returns a channel where the 204 // receiving peer is sent once the request has been sent (request callback returned). 205 // If the request is cancelled or timed out without suitable peers, the channel is 206 // closed without sending any peer references to it. 207 func (d *requestDistributor) queue(r *distReq) chan distPeer { 208 d.lock.Lock() 209 defer d.lock.Unlock() 210 211 if r.reqOrder == 0 { 212 d.lastReqOrder++ 213 r.reqOrder = d.lastReqOrder 214 } 215 216 back := d.reqQueue.Back() 217 if back == nil || r.reqOrder > back.Value.(*distReq).reqOrder { 218 r.element = d.reqQueue.PushBack(r) 219 } else { 220 before := d.reqQueue.Front() 221 for before.Value.(*distReq).reqOrder < r.reqOrder { 222 before = before.Next() 223 } 224 r.element = d.reqQueue.InsertBefore(r, before) 225 } 226 227 if !d.loopNextSent { 228 d.loopNextSent = true 229 d.loopChn <- struct{}{} 230 } 231 232 r.sentChn = make(chan distPeer, 1) 233 return r.sentChn 234 } 235 236 // cancel removes a request from the queue if it has not been sent yet (returns 237 // false if it has been sent already). It is guaranteed that the callback functions 238 // will not be called after cancel returns. 239 func (d *requestDistributor) cancel(r *distReq) bool { 240 d.lock.Lock() 241 defer d.lock.Unlock() 242 243 if r.sentChn == nil { 244 return false 245 } 246 247 close(r.sentChn) 248 d.remove(r) 249 return true 250 } 251 252 // remove removes a request from the queue 253 func (d *requestDistributor) remove(r *distReq) { 254 r.sentChn = nil 255 if r.element != nil { 256 d.reqQueue.Remove(r.element) 257 r.element = nil 258 } 259 }