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