github.com/m3shine/gochain@v2.2.26+incompatible/les/distributor_test.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 "context" 23 "math/rand" 24 "sync" 25 "testing" 26 "time" 27 ) 28 29 type testDistReq struct { 30 cost, procTime, order uint64 31 canSendTo map[*testDistPeer]struct{} 32 } 33 34 func (r *testDistReq) getCost(dp distPeer) uint64 { 35 return r.cost 36 } 37 38 func (r *testDistReq) canSend(dp distPeer) bool { 39 _, ok := r.canSendTo[dp.(*testDistPeer)] 40 return ok 41 } 42 43 func (r *testDistReq) request(dp distPeer) func(context.Context) { 44 return func(context.Context) { dp.(*testDistPeer).send(r) } 45 } 46 47 type testDistPeer struct { 48 sent []*testDistReq 49 sumCost uint64 50 lock sync.RWMutex 51 } 52 53 func (p *testDistPeer) send(r *testDistReq) { 54 p.lock.Lock() 55 defer p.lock.Unlock() 56 57 p.sent = append(p.sent, r) 58 p.sumCost += r.cost 59 } 60 61 func (p *testDistPeer) worker(t *testing.T, checkOrder bool, stop chan struct{}) { 62 var last uint64 63 for { 64 wait := time.Millisecond 65 p.lock.Lock() 66 if len(p.sent) > 0 { 67 rq := p.sent[0] 68 wait = time.Duration(rq.procTime) 69 p.sumCost -= rq.cost 70 if checkOrder { 71 if rq.order <= last { 72 t.Errorf("Requests processed in wrong order") 73 } 74 last = rq.order 75 } 76 p.sent = p.sent[1:] 77 } 78 p.lock.Unlock() 79 select { 80 case <-stop: 81 return 82 case <-time.After(wait): 83 } 84 } 85 } 86 87 const ( 88 testDistBufLimit = 10000000 89 testDistMaxCost = 1000000 90 testDistPeerCount = 5 91 testDistReqCount = 50000 92 testDistMaxResendCount = 3 93 ) 94 95 func (p *testDistPeer) waitBefore(cost uint64) (time.Duration, float64) { 96 p.lock.RLock() 97 sumCost := p.sumCost + cost 98 p.lock.RUnlock() 99 if sumCost < testDistBufLimit { 100 return 0, float64(testDistBufLimit-sumCost) / float64(testDistBufLimit) 101 } else { 102 return time.Duration(sumCost - testDistBufLimit), 0 103 } 104 } 105 106 func (p *testDistPeer) canQueue() bool { 107 return true 108 } 109 110 func (p *testDistPeer) queueSend(f func(context.Context)) { 111 f(context.Background()) 112 } 113 114 func TestRequestDistributor(t *testing.T) { 115 testRequestDistributor(t, false) 116 } 117 118 func TestRequestDistributorResend(t *testing.T) { 119 testRequestDistributor(t, true) 120 } 121 122 func testRequestDistributor(t *testing.T, resend bool) { 123 stop := make(chan struct{}) 124 defer close(stop) 125 126 dist := newRequestDistributor(nil, stop) 127 var peers [testDistPeerCount]*testDistPeer 128 for i := range peers { 129 peers[i] = &testDistPeer{} 130 go peers[i].worker(t, !resend, stop) 131 dist.registerTestPeer(peers[i]) 132 } 133 134 var wg sync.WaitGroup 135 136 for i := 1; i <= testDistReqCount; i++ { 137 cost := uint64(rand.Int63n(testDistMaxCost)) 138 procTime := uint64(rand.Int63n(int64(cost + 1))) 139 rq := &testDistReq{ 140 cost: cost, 141 procTime: procTime, 142 order: uint64(i), 143 canSendTo: make(map[*testDistPeer]struct{}), 144 } 145 for _, peer := range peers { 146 if rand.Intn(2) != 0 { 147 rq.canSendTo[peer] = struct{}{} 148 } 149 } 150 151 wg.Add(1) 152 req := &distReq{ 153 getCost: rq.getCost, 154 canSend: rq.canSend, 155 request: rq.request, 156 } 157 chn := dist.queue(req) 158 go func() { 159 cnt := 1 160 if resend && len(rq.canSendTo) != 0 { 161 cnt = rand.Intn(testDistMaxResendCount) + 1 162 } 163 for i := 0; i < cnt; i++ { 164 if i != 0 { 165 chn = dist.queue(req) 166 } 167 p := <-chn 168 if p == nil { 169 if len(rq.canSendTo) != 0 { 170 t.Errorf("Request that could have been sent was dropped") 171 } 172 } else { 173 peer := p.(*testDistPeer) 174 if _, ok := rq.canSendTo[peer]; !ok { 175 t.Errorf("Request sent to wrong peer") 176 } 177 } 178 } 179 wg.Done() 180 }() 181 if rand.Intn(1000) == 0 { 182 time.Sleep(time.Duration(rand.Intn(5000000))) 183 } 184 } 185 186 wg.Wait() 187 }