github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/les/distributor.go (about) 1 // Copyright 2017 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum 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 peers map[distPeer]struct{} 38 peerLock sync.RWMutex 39 stopChn, loopChn chan struct{} 40 loopNextSent bool 41 lock sync.Mutex 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(peers *peerSet, stopChn chan struct{}) *requestDistributor { 75 d := &requestDistributor{ 76 reqQueue: list.New(), 77 loopChn: make(chan struct{}, 2), 78 stopChn: stopChn, 79 peers: make(map[distPeer]struct{}), 80 } 81 if peers != nil { 82 peers.notify(d) 83 } 84 go d.loop() 85 return d 86 } 87 88 // registerPeer implements peerSetNotify 89 func (d *requestDistributor) registerPeer(p *peer) { 90 d.peerLock.Lock() 91 d.peers[p] = struct{}{} 92 d.peerLock.Unlock() 93 } 94 95 // unregisterPeer implements peerSetNotify 96 func (d *requestDistributor) unregisterPeer(p *peer) { 97 d.peerLock.Lock() 98 delete(d.peers, p) 99 d.peerLock.Unlock() 100 } 101 102 // registerTestPeer adds a new test peer 103 func (d *requestDistributor) registerTestPeer(p distPeer) { 104 d.peerLock.Lock() 105 d.peers[p] = struct{}{} 106 d.peerLock.Unlock() 107 } 108 109 // distMaxWait is the maximum waiting time after which further necessary waiting 110 // times are recalculated based on new feedback from the servers 111 const distMaxWait = time.Millisecond * 10 112 113 // main event loop 114 func (d *requestDistributor) loop() { 115 for { 116 select { 117 case <-d.stopChn: 118 d.lock.Lock() 119 elem := d.reqQueue.Front() 120 for elem != nil { 121 close(elem.Value.(*distReq).sentChn) 122 elem = elem.Next() 123 } 124 d.lock.Unlock() 125 return 126 case <-d.loopChn: 127 d.lock.Lock() 128 d.loopNextSent = false 129 loop: 130 for { 131 peer, req, wait := d.nextRequest() 132 if req != nil && wait == 0 { 133 chn := req.sentChn // save sentChn because remove sets it to nil 134 d.remove(req) 135 send := req.request(peer) 136 if send != nil { 137 peer.queueSend(send) 138 } 139 chn <- peer 140 close(chn) 141 } else { 142 if wait == 0 { 143 // no request to send and nothing to wait for; the next 144 // queued request will wake up the loop 145 break loop 146 } 147 d.loopNextSent = true // a "next" signal has been sent, do not send another one until this one has been received 148 if wait > distMaxWait { 149 // waiting times may be reduced by incoming request replies, if it is too long, recalculate it periodically 150 wait = distMaxWait 151 } 152 go func() { 153 time.Sleep(wait) 154 d.loopChn <- struct{}{} 155 }() 156 break loop 157 } 158 } 159 d.lock.Unlock() 160 } 161 } 162 } 163 164 // selectPeerItem represents a peer to be selected for a request by weightedRandomSelect 165 type selectPeerItem struct { 166 peer distPeer 167 req *distReq 168 weight int64 169 } 170 171 // Weight implements wrsItem interface 172 func (sp selectPeerItem) Weight() int64 { 173 return sp.weight 174 } 175 176 // nextRequest returns the next possible request from any peer, along with the 177 // associated peer and necessary waiting time 178 func (d *requestDistributor) nextRequest() (distPeer, *distReq, time.Duration) { 179 checkedPeers := make(map[distPeer]struct{}) 180 elem := d.reqQueue.Front() 181 var ( 182 bestPeer distPeer 183 bestReq *distReq 184 bestWait time.Duration 185 sel *weightedRandomSelect 186 ) 187 188 d.peerLock.RLock() 189 defer d.peerLock.RUnlock() 190 191 for (len(d.peers) > 0 || elem == d.reqQueue.Front()) && elem != nil { 192 req := elem.Value.(*distReq) 193 canSend := false 194 for peer := range d.peers { 195 if _, ok := checkedPeers[peer]; !ok && peer.canQueue() && req.canSend(peer) { 196 canSend = true 197 cost := req.getCost(peer) 198 wait, bufRemain := peer.waitBefore(cost) 199 if wait == 0 { 200 if sel == nil { 201 sel = newWeightedRandomSelect() 202 } 203 sel.update(selectPeerItem{peer: peer, req: req, weight: int64(bufRemain*1000000) + 1}) 204 } else { 205 if bestReq == nil || wait < bestWait { 206 bestPeer = peer 207 bestReq = req 208 bestWait = wait 209 } 210 } 211 checkedPeers[peer] = struct{}{} 212 } 213 } 214 next := elem.Next() 215 if !canSend && elem == d.reqQueue.Front() { 216 close(req.sentChn) 217 d.remove(req) 218 } 219 elem = next 220 } 221 222 if sel != nil { 223 c := sel.choose().(selectPeerItem) 224 return c.peer, c.req, 0 225 } 226 return bestPeer, bestReq, bestWait 227 } 228 229 // queue adds a request to the distribution queue, returns a channel where the 230 // receiving peer is sent once the request has been sent (request callback returned). 231 // If the request is cancelled or timed out without suitable peers, the channel is 232 // closed without sending any peer references to it. 233 func (d *requestDistributor) queue(r *distReq) chan distPeer { 234 d.lock.Lock() 235 defer d.lock.Unlock() 236 237 if r.reqOrder == 0 { 238 d.lastReqOrder++ 239 r.reqOrder = d.lastReqOrder 240 } 241 242 back := d.reqQueue.Back() 243 if back == nil || r.reqOrder > back.Value.(*distReq).reqOrder { 244 r.element = d.reqQueue.PushBack(r) 245 } else { 246 before := d.reqQueue.Front() 247 for before.Value.(*distReq).reqOrder < r.reqOrder { 248 before = before.Next() 249 } 250 r.element = d.reqQueue.InsertBefore(r, before) 251 } 252 253 if !d.loopNextSent { 254 d.loopNextSent = true 255 d.loopChn <- struct{}{} 256 } 257 258 r.sentChn = make(chan distPeer, 1) 259 return r.sentChn 260 } 261 262 // cancel removes a request from the queue if it has not been sent yet (returns 263 // false if it has been sent already). It is guaranteed that the callback functions 264 // will not be called after cancel returns. 265 func (d *requestDistributor) cancel(r *distReq) bool { 266 d.lock.Lock() 267 defer d.lock.Unlock() 268 269 if r.sentChn == nil { 270 return false 271 } 272 273 close(r.sentChn) 274 d.remove(r) 275 return true 276 } 277 278 // remove removes a request from the queue 279 func (d *requestDistributor) remove(r *distReq) { 280 r.sentChn = nil 281 if r.element != nil { 282 d.reqQueue.Remove(r.element) 283 r.element = nil 284 } 285 }