github.com/bobanetwork/go-ethereum@v1.9.7/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 les 18 19 import ( 20 "container/list" 21 "sync" 22 "time" 23 24 "github.com/ethereum/go-ethereum/common/mclock" 25 ) 26 27 // requestDistributor implements a mechanism that distributes requests to 28 // suitable peers, obeying flow control rules and prioritizing them in creation 29 // order (even when a resend is necessary). 30 type requestDistributor struct { 31 clock mclock.Clock 32 reqQueue *list.List 33 lastReqOrder uint64 34 peers map[distPeer]struct{} 35 peerLock sync.RWMutex 36 loopChn chan struct{} 37 loopNextSent bool 38 lock sync.Mutex 39 40 closeCh chan struct{} 41 wg sync.WaitGroup 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 waitForPeers mclock.AbsTime 72 enterQueue mclock.AbsTime 73 } 74 75 // newRequestDistributor creates a new request distributor 76 func newRequestDistributor(peers *peerSet, clock mclock.Clock) *requestDistributor { 77 d := &requestDistributor{ 78 clock: clock, 79 reqQueue: list.New(), 80 loopChn: make(chan struct{}, 2), 81 closeCh: make(chan struct{}), 82 peers: make(map[distPeer]struct{}), 83 } 84 if peers != nil { 85 peers.notify(d) 86 } 87 d.wg.Add(1) 88 go d.loop() 89 return d 90 } 91 92 // registerPeer implements peerSetNotify 93 func (d *requestDistributor) registerPeer(p *peer) { 94 d.peerLock.Lock() 95 d.peers[p] = struct{}{} 96 d.peerLock.Unlock() 97 } 98 99 // unregisterPeer implements peerSetNotify 100 func (d *requestDistributor) unregisterPeer(p *peer) { 101 d.peerLock.Lock() 102 delete(d.peers, p) 103 d.peerLock.Unlock() 104 } 105 106 // registerTestPeer adds a new test peer 107 func (d *requestDistributor) registerTestPeer(p distPeer) { 108 d.peerLock.Lock() 109 d.peers[p] = struct{}{} 110 d.peerLock.Unlock() 111 } 112 113 var ( 114 // distMaxWait is the maximum waiting time after which further necessary waiting 115 // times are recalculated based on new feedback from the servers 116 distMaxWait = time.Millisecond * 50 117 118 // waitForPeers is the time window in which a request does not fail even if it 119 // has no suitable peers to send to at the moment 120 waitForPeers = time.Second * 3 121 ) 122 123 // main event loop 124 func (d *requestDistributor) loop() { 125 defer d.wg.Done() 126 for { 127 select { 128 case <-d.closeCh: 129 d.lock.Lock() 130 elem := d.reqQueue.Front() 131 for elem != nil { 132 req := elem.Value.(*distReq) 133 close(req.sentChn) 134 req.sentChn = nil 135 elem = elem.Next() 136 } 137 d.lock.Unlock() 138 return 139 case <-d.loopChn: 140 d.lock.Lock() 141 d.loopNextSent = false 142 loop: 143 for { 144 peer, req, wait := d.nextRequest() 145 if req != nil && wait == 0 { 146 chn := req.sentChn // save sentChn because remove sets it to nil 147 d.remove(req) 148 send := req.request(peer) 149 if send != nil { 150 peer.queueSend(send) 151 requestSendDelay.Update(time.Duration(d.clock.Now() - req.enterQueue)) 152 } 153 chn <- peer 154 close(chn) 155 } else { 156 if wait == 0 { 157 // no request to send and nothing to wait for; the next 158 // queued request will wake up the loop 159 break loop 160 } 161 d.loopNextSent = true // a "next" signal has been sent, do not send another one until this one has been received 162 if wait > distMaxWait { 163 // waiting times may be reduced by incoming request replies, if it is too long, recalculate it periodically 164 wait = distMaxWait 165 } 166 go func() { 167 d.clock.Sleep(wait) 168 d.loopChn <- struct{}{} 169 }() 170 break loop 171 } 172 } 173 d.lock.Unlock() 174 } 175 } 176 } 177 178 // selectPeerItem represents a peer to be selected for a request by weightedRandomSelect 179 type selectPeerItem struct { 180 peer distPeer 181 req *distReq 182 weight int64 183 } 184 185 // Weight implements wrsItem interface 186 func (sp selectPeerItem) Weight() int64 { 187 return sp.weight 188 } 189 190 // nextRequest returns the next possible request from any peer, along with the 191 // associated peer and necessary waiting time 192 func (d *requestDistributor) nextRequest() (distPeer, *distReq, time.Duration) { 193 checkedPeers := make(map[distPeer]struct{}) 194 elem := d.reqQueue.Front() 195 var ( 196 bestWait time.Duration 197 sel *weightedRandomSelect 198 ) 199 200 d.peerLock.RLock() 201 defer d.peerLock.RUnlock() 202 203 peerCount := len(d.peers) 204 for (len(checkedPeers) < peerCount || elem == d.reqQueue.Front()) && elem != nil { 205 req := elem.Value.(*distReq) 206 canSend := false 207 now := d.clock.Now() 208 if req.waitForPeers > now { 209 canSend = true 210 wait := time.Duration(req.waitForPeers - now) 211 if bestWait == 0 || wait < bestWait { 212 bestWait = wait 213 } 214 } 215 for peer := range d.peers { 216 if _, ok := checkedPeers[peer]; !ok && peer.canQueue() && req.canSend(peer) { 217 canSend = true 218 cost := req.getCost(peer) 219 wait, bufRemain := peer.waitBefore(cost) 220 if wait == 0 { 221 if sel == nil { 222 sel = newWeightedRandomSelect() 223 } 224 sel.update(selectPeerItem{peer: peer, req: req, weight: int64(bufRemain*1000000) + 1}) 225 } else { 226 if bestWait == 0 || wait < bestWait { 227 bestWait = wait 228 } 229 } 230 checkedPeers[peer] = struct{}{} 231 } 232 } 233 next := elem.Next() 234 if !canSend && elem == d.reqQueue.Front() { 235 close(req.sentChn) 236 d.remove(req) 237 } 238 elem = next 239 } 240 241 if sel != nil { 242 c := sel.choose().(selectPeerItem) 243 return c.peer, c.req, 0 244 } 245 return nil, nil, bestWait 246 } 247 248 // queue adds a request to the distribution queue, returns a channel where the 249 // receiving peer is sent once the request has been sent (request callback returned). 250 // If the request is cancelled or timed out without suitable peers, the channel is 251 // closed without sending any peer references to it. 252 func (d *requestDistributor) queue(r *distReq) chan distPeer { 253 d.lock.Lock() 254 defer d.lock.Unlock() 255 256 if r.reqOrder == 0 { 257 d.lastReqOrder++ 258 r.reqOrder = d.lastReqOrder 259 r.waitForPeers = d.clock.Now() + mclock.AbsTime(waitForPeers) 260 } 261 // Assign the timestamp when the request is queued no matter it's 262 // a new one or re-queued one. 263 r.enterQueue = d.clock.Now() 264 265 back := d.reqQueue.Back() 266 if back == nil || r.reqOrder > back.Value.(*distReq).reqOrder { 267 r.element = d.reqQueue.PushBack(r) 268 } else { 269 before := d.reqQueue.Front() 270 for before.Value.(*distReq).reqOrder < r.reqOrder { 271 before = before.Next() 272 } 273 r.element = d.reqQueue.InsertBefore(r, before) 274 } 275 276 if !d.loopNextSent { 277 d.loopNextSent = true 278 d.loopChn <- struct{}{} 279 } 280 281 r.sentChn = make(chan distPeer, 1) 282 return r.sentChn 283 } 284 285 // cancel removes a request from the queue if it has not been sent yet (returns 286 // false if it has been sent already). It is guaranteed that the callback functions 287 // will not be called after cancel returns. 288 func (d *requestDistributor) cancel(r *distReq) bool { 289 d.lock.Lock() 290 defer d.lock.Unlock() 291 292 if r.sentChn == nil { 293 return false 294 } 295 296 close(r.sentChn) 297 d.remove(r) 298 return true 299 } 300 301 // remove removes a request from the queue 302 func (d *requestDistributor) remove(r *distReq) { 303 r.sentChn = nil 304 if r.element != nil { 305 d.reqQueue.Remove(r.element) 306 r.element = nil 307 } 308 } 309 310 func (d *requestDistributor) close() { 311 close(d.closeCh) 312 d.wg.Wait() 313 }