github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/test/e2e/runner/load.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"errors"
     7  	"fmt"
     8  	"math"
     9  	"time"
    10  
    11  	rpchttp "github.com/lazyledger/lazyledger-core/rpc/client/http"
    12  	e2e "github.com/lazyledger/lazyledger-core/test/e2e/pkg"
    13  	"github.com/lazyledger/lazyledger-core/types"
    14  )
    15  
    16  // Load generates transactions against the network until the given
    17  // context is cancelled.
    18  func Load(ctx context.Context, testnet *e2e.Testnet) error {
    19  	// Since transactions are executed across all nodes in the network, we need
    20  	// to reduce transaction load for larger networks to avoid using too much
    21  	// CPU. This gives high-throughput small networks and low-throughput large ones.
    22  	// This also limits the number of TCP connections, since each worker has
    23  	// a connection to all nodes.
    24  	concurrency := 64 / len(testnet.Nodes)
    25  	if concurrency == 0 {
    26  		concurrency = 1
    27  	}
    28  	initialTimeout := 5 * time.Minute
    29  	stallTimeout := 30 * time.Second
    30  
    31  	chTx := make(chan types.Tx)
    32  	chSuccess := make(chan types.Tx)
    33  	ctx, cancel := context.WithCancel(ctx)
    34  	defer cancel()
    35  
    36  	// Spawn job generator and processors.
    37  	logger.Info(fmt.Sprintf("Starting transaction load (%v workers)...", concurrency))
    38  	started := time.Now()
    39  
    40  	go loadGenerate(ctx, chTx)
    41  
    42  	for w := 0; w < concurrency; w++ {
    43  		go loadProcess(ctx, testnet, chTx, chSuccess)
    44  	}
    45  
    46  	// Monitor successful transactions, and abort on stalls.
    47  	success := 0
    48  	timeout := initialTimeout
    49  	for {
    50  		select {
    51  		case <-chSuccess:
    52  			success++
    53  			timeout = stallTimeout
    54  		case <-time.After(timeout):
    55  			return fmt.Errorf("unable to submit transactions for %v", timeout)
    56  		case <-ctx.Done():
    57  			if success == 0 {
    58  				return errors.New("failed to submit any transactions")
    59  			}
    60  			logger.Info(fmt.Sprintf("Ending transaction load after %v txs (%.1f tx/s)...",
    61  				success, float64(success)/time.Since(started).Seconds()))
    62  			return nil
    63  		}
    64  	}
    65  }
    66  
    67  // loadGenerate generates jobs until the context is cancelled
    68  func loadGenerate(ctx context.Context, chTx chan<- types.Tx) {
    69  	for i := 0; i < math.MaxInt64; i++ {
    70  		// We keep generating the same 1000 keys over and over, with different values.
    71  		// This gives a reasonable load without putting too much data in the app.
    72  		id := i % 1000
    73  
    74  		bz := make([]byte, 2048) // 4kb hex-encoded
    75  		_, err := rand.Read(bz)
    76  		if err != nil {
    77  			panic(fmt.Sprintf("Failed to read random bytes: %v", err))
    78  		}
    79  		tx := types.Tx(fmt.Sprintf("load-%X=%x", id, bz))
    80  
    81  		select {
    82  		case chTx <- tx:
    83  			time.Sleep(10 * time.Millisecond)
    84  		case <-ctx.Done():
    85  			close(chTx)
    86  			return
    87  		}
    88  	}
    89  }
    90  
    91  // loadProcess processes transactions
    92  func loadProcess(ctx context.Context, testnet *e2e.Testnet, chTx <-chan types.Tx, chSuccess chan<- types.Tx) {
    93  	// Each worker gets its own client to each node, which allows for some
    94  	// concurrency while still bounding it.
    95  	clients := map[string]*rpchttp.HTTP{}
    96  
    97  	var err error
    98  	for tx := range chTx {
    99  		node := testnet.RandomNode()
   100  		client, ok := clients[node.Name]
   101  		if !ok {
   102  			client, err = node.Client()
   103  			if err != nil {
   104  				continue
   105  			}
   106  			clients[node.Name] = client
   107  		}
   108  		_, err = client.BroadcastTxCommit(ctx, tx)
   109  		if err != nil {
   110  			continue
   111  		}
   112  		chSuccess <- tx
   113  	}
   114  }