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  }