github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/les/vflux/client/serverpool_test.go (about)

     1  // Copyright 2021 The adkgo Authors
     2  // This file is part of the adkgo library (adapted for adkgo from go--ethereum v1.10.8).
     3  //
     4  // the adkgo 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 adkgo 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 adkgo library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package client
    18  
    19  import (
    20  	"math/rand"
    21  	"strconv"
    22  	"sync/atomic"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/aidoskuneen/adk-node/common/mclock"
    27  	"github.com/aidoskuneen/adk-node/ethdb"
    28  	"github.com/aidoskuneen/adk-node/ethdb/memorydb"
    29  	"github.com/aidoskuneen/adk-node/p2p/enode"
    30  	"github.com/aidoskuneen/adk-node/p2p/enr"
    31  )
    32  
    33  const (
    34  	spTestNodes  = 1000
    35  	spTestTarget = 5
    36  	spTestLength = 10000
    37  	spMinTotal   = 40000
    38  	spMaxTotal   = 50000
    39  )
    40  
    41  func testNodeID(i int) enode.ID {
    42  	return enode.ID{42, byte(i % 256), byte(i / 256)}
    43  }
    44  
    45  func testNodeIndex(id enode.ID) int {
    46  	if id[0] != 42 {
    47  		return -1
    48  	}
    49  	return int(id[1]) + int(id[2])*256
    50  }
    51  
    52  type ServerPoolTest struct {
    53  	db                   ethdb.KeyValueStore
    54  	clock                *mclock.Simulated
    55  	quit                 chan struct{}
    56  	preNeg, preNegFail   bool
    57  	vt                   *ValueTracker
    58  	sp                   *ServerPool
    59  	spi                  enode.Iterator
    60  	input                enode.Iterator
    61  	testNodes            []spTestNode
    62  	trusted              []string
    63  	waitCount, waitEnded int32
    64  
    65  	cycle, conn, servedConn  int
    66  	serviceCycles, dialCount int
    67  	disconnect               map[int][]int
    68  }
    69  
    70  type spTestNode struct {
    71  	connectCycles, waitCycles int
    72  	nextConnCycle, totalConn  int
    73  	connected, service        bool
    74  	node                      *enode.Node
    75  }
    76  
    77  func newServerPoolTest(preNeg, preNegFail bool) *ServerPoolTest {
    78  	nodes := make([]*enode.Node, spTestNodes)
    79  	for i := range nodes {
    80  		nodes[i] = enode.SignNull(&enr.Record{}, testNodeID(i))
    81  	}
    82  	return &ServerPoolTest{
    83  		clock:      &mclock.Simulated{},
    84  		db:         memorydb.New(),
    85  		input:      enode.CycleNodes(nodes),
    86  		testNodes:  make([]spTestNode, spTestNodes),
    87  		preNeg:     preNeg,
    88  		preNegFail: preNegFail,
    89  	}
    90  }
    91  
    92  func (s *ServerPoolTest) beginWait() {
    93  	// ensure that dialIterator and the maximal number of pre-neg queries are not all stuck in a waiting state
    94  	for atomic.AddInt32(&s.waitCount, 1) > preNegLimit {
    95  		atomic.AddInt32(&s.waitCount, -1)
    96  		s.clock.Run(time.Second)
    97  	}
    98  }
    99  
   100  func (s *ServerPoolTest) endWait() {
   101  	atomic.AddInt32(&s.waitCount, -1)
   102  	atomic.AddInt32(&s.waitEnded, 1)
   103  }
   104  
   105  func (s *ServerPoolTest) addTrusted(i int) {
   106  	s.trusted = append(s.trusted, enode.SignNull(&enr.Record{}, testNodeID(i)).String())
   107  }
   108  
   109  func (s *ServerPoolTest) start() {
   110  	var testQuery QueryFunc
   111  	if s.preNeg {
   112  		testQuery = func(node *enode.Node) int {
   113  			idx := testNodeIndex(node.ID())
   114  			n := &s.testNodes[idx]
   115  			canConnect := !n.connected && n.connectCycles != 0 && s.cycle >= n.nextConnCycle
   116  			if s.preNegFail {
   117  				// simulate a scenario where UDP queries never work
   118  				s.beginWait()
   119  				s.clock.Sleep(time.Second * 5)
   120  				s.endWait()
   121  				return -1
   122  			}
   123  			switch idx % 3 {
   124  			case 0:
   125  				// pre-neg returns true only if connection is possible
   126  				if canConnect {
   127  					return 1
   128  				}
   129  				return 0
   130  			case 1:
   131  				// pre-neg returns true but connection might still fail
   132  				return 1
   133  			case 2:
   134  				// pre-neg returns true if connection is possible, otherwise timeout (node unresponsive)
   135  				if canConnect {
   136  					return 1
   137  				}
   138  				s.beginWait()
   139  				s.clock.Sleep(time.Second * 5)
   140  				s.endWait()
   141  				return -1
   142  			}
   143  			return -1
   144  		}
   145  	}
   146  
   147  	requestList := make([]RequestInfo, testReqTypes)
   148  	for i := range requestList {
   149  		requestList[i] = RequestInfo{Name: "testreq" + strconv.Itoa(i), InitAmount: 1, InitValue: 1}
   150  	}
   151  
   152  	s.sp, s.spi = NewServerPool(s.db, []byte("sp:"), 0, testQuery, s.clock, s.trusted, requestList)
   153  	s.sp.AddSource(s.input)
   154  	s.sp.validSchemes = enode.ValidSchemesForTesting
   155  	s.sp.unixTime = func() int64 { return int64(s.clock.Now()) / int64(time.Second) }
   156  	s.disconnect = make(map[int][]int)
   157  	s.sp.Start()
   158  	s.quit = make(chan struct{})
   159  	go func() {
   160  		last := int32(-1)
   161  		for {
   162  			select {
   163  			case <-time.After(time.Millisecond * 100):
   164  				c := atomic.LoadInt32(&s.waitEnded)
   165  				if c == last {
   166  					// advance clock if test is stuck (might happen in rare cases)
   167  					s.clock.Run(time.Second)
   168  				}
   169  				last = c
   170  			case <-s.quit:
   171  				return
   172  			}
   173  		}
   174  	}()
   175  }
   176  
   177  func (s *ServerPoolTest) stop() {
   178  	close(s.quit)
   179  	s.sp.Stop()
   180  	s.spi.Close()
   181  	for i := range s.testNodes {
   182  		n := &s.testNodes[i]
   183  		if n.connected {
   184  			n.totalConn += s.cycle
   185  		}
   186  		n.connected = false
   187  		n.node = nil
   188  		n.nextConnCycle = 0
   189  	}
   190  	s.conn, s.servedConn = 0, 0
   191  }
   192  
   193  func (s *ServerPoolTest) run() {
   194  	for count := spTestLength; count > 0; count-- {
   195  		if dcList := s.disconnect[s.cycle]; dcList != nil {
   196  			for _, idx := range dcList {
   197  				n := &s.testNodes[idx]
   198  				s.sp.UnregisterNode(n.node)
   199  				n.totalConn += s.cycle
   200  				n.connected = false
   201  				n.node = nil
   202  				s.conn--
   203  				if n.service {
   204  					s.servedConn--
   205  				}
   206  				n.nextConnCycle = s.cycle + n.waitCycles
   207  			}
   208  			delete(s.disconnect, s.cycle)
   209  		}
   210  		if s.conn < spTestTarget {
   211  			s.dialCount++
   212  			s.beginWait()
   213  			s.spi.Next()
   214  			s.endWait()
   215  			dial := s.spi.Node()
   216  			id := dial.ID()
   217  			idx := testNodeIndex(id)
   218  			n := &s.testNodes[idx]
   219  			if !n.connected && n.connectCycles != 0 && s.cycle >= n.nextConnCycle {
   220  				s.conn++
   221  				if n.service {
   222  					s.servedConn++
   223  				}
   224  				n.totalConn -= s.cycle
   225  				n.connected = true
   226  				dc := s.cycle + n.connectCycles
   227  				s.disconnect[dc] = append(s.disconnect[dc], idx)
   228  				n.node = dial
   229  				nv, _ := s.sp.RegisterNode(n.node)
   230  				if n.service {
   231  					nv.Served([]ServedRequest{{ReqType: 0, Amount: 100}}, 0)
   232  				}
   233  			}
   234  		}
   235  		s.serviceCycles += s.servedConn
   236  		s.clock.Run(time.Second)
   237  		s.cycle++
   238  	}
   239  }
   240  
   241  func (s *ServerPoolTest) setNodes(count, conn, wait int, service, trusted bool) (res []int) {
   242  	for ; count > 0; count-- {
   243  		idx := rand.Intn(spTestNodes)
   244  		for s.testNodes[idx].connectCycles != 0 || s.testNodes[idx].connected {
   245  			idx = rand.Intn(spTestNodes)
   246  		}
   247  		res = append(res, idx)
   248  		s.testNodes[idx] = spTestNode{
   249  			connectCycles: conn,
   250  			waitCycles:    wait,
   251  			service:       service,
   252  		}
   253  		if trusted {
   254  			s.addTrusted(idx)
   255  		}
   256  	}
   257  	return
   258  }
   259  
   260  func (s *ServerPoolTest) resetNodes() {
   261  	for i, n := range s.testNodes {
   262  		if n.connected {
   263  			n.totalConn += s.cycle
   264  			s.sp.UnregisterNode(n.node)
   265  		}
   266  		s.testNodes[i] = spTestNode{totalConn: n.totalConn}
   267  	}
   268  	s.conn, s.servedConn = 0, 0
   269  	s.disconnect = make(map[int][]int)
   270  	s.trusted = nil
   271  }
   272  
   273  func (s *ServerPoolTest) checkNodes(t *testing.T, nodes []int) {
   274  	var sum int
   275  	for _, idx := range nodes {
   276  		n := &s.testNodes[idx]
   277  		if n.connected {
   278  			n.totalConn += s.cycle
   279  		}
   280  		sum += n.totalConn
   281  		n.totalConn = 0
   282  		if n.connected {
   283  			n.totalConn -= s.cycle
   284  		}
   285  	}
   286  	if sum < spMinTotal || sum > spMaxTotal {
   287  		t.Errorf("Total connection amount %d outside expected range %d to %d", sum, spMinTotal, spMaxTotal)
   288  	}
   289  }
   290  
   291  func TestServerPool(t *testing.T)               { testServerPool(t, false, false) }
   292  func TestServerPoolWithPreNeg(t *testing.T)     { testServerPool(t, true, false) }
   293  func TestServerPoolWithPreNegFail(t *testing.T) { testServerPool(t, true, true) }
   294  func testServerPool(t *testing.T, preNeg, fail bool) {
   295  	s := newServerPoolTest(preNeg, fail)
   296  	nodes := s.setNodes(100, 200, 200, true, false)
   297  	s.setNodes(100, 20, 20, false, false)
   298  	s.start()
   299  	s.run()
   300  	s.stop()
   301  	s.checkNodes(t, nodes)
   302  }
   303  
   304  func TestServerPoolChangedNodes(t *testing.T)           { testServerPoolChangedNodes(t, false) }
   305  func TestServerPoolChangedNodesWithPreNeg(t *testing.T) { testServerPoolChangedNodes(t, true) }
   306  func testServerPoolChangedNodes(t *testing.T, preNeg bool) {
   307  	s := newServerPoolTest(preNeg, false)
   308  	nodes := s.setNodes(100, 200, 200, true, false)
   309  	s.setNodes(100, 20, 20, false, false)
   310  	s.start()
   311  	s.run()
   312  	s.checkNodes(t, nodes)
   313  	for i := 0; i < 3; i++ {
   314  		s.resetNodes()
   315  		nodes := s.setNodes(100, 200, 200, true, false)
   316  		s.setNodes(100, 20, 20, false, false)
   317  		s.run()
   318  		s.checkNodes(t, nodes)
   319  	}
   320  	s.stop()
   321  }
   322  
   323  func TestServerPoolRestartNoDiscovery(t *testing.T) { testServerPoolRestartNoDiscovery(t, false) }
   324  func TestServerPoolRestartNoDiscoveryWithPreNeg(t *testing.T) {
   325  	testServerPoolRestartNoDiscovery(t, true)
   326  }
   327  func testServerPoolRestartNoDiscovery(t *testing.T, preNeg bool) {
   328  	s := newServerPoolTest(preNeg, false)
   329  	nodes := s.setNodes(100, 200, 200, true, false)
   330  	s.setNodes(100, 20, 20, false, false)
   331  	s.start()
   332  	s.run()
   333  	s.stop()
   334  	s.checkNodes(t, nodes)
   335  	s.input = nil
   336  	s.start()
   337  	s.run()
   338  	s.stop()
   339  	s.checkNodes(t, nodes)
   340  }
   341  
   342  func TestServerPoolTrustedNoDiscovery(t *testing.T) { testServerPoolTrustedNoDiscovery(t, false) }
   343  func TestServerPoolTrustedNoDiscoveryWithPreNeg(t *testing.T) {
   344  	testServerPoolTrustedNoDiscovery(t, true)
   345  }
   346  func testServerPoolTrustedNoDiscovery(t *testing.T, preNeg bool) {
   347  	s := newServerPoolTest(preNeg, false)
   348  	trusted := s.setNodes(200, 200, 200, true, true)
   349  	s.input = nil
   350  	s.start()
   351  	s.run()
   352  	s.stop()
   353  	s.checkNodes(t, trusted)
   354  }