load

package
v1.13.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2025 License: BSD-3-Clause Imports: 11 Imported by: 0

README

Load

This package provides generic utilities for blockchain load testing. We break load generation down into the following components:

  • tx issuer(s)
  • tx listener(s)
  • tracker
  • orchestrator

The transaction issuer(s) and listener(s) may be VM specific and provide the necessary injected dependencies for the orchestrator. This enables us to construct different load testing strategies on top of the same re-usable code. For example, we can re-use these components for a short burst of transactions or to perform gradual load testing.

Architecture

graph
    O["Orchestrator"]
    subgraph "Agent 3"
        A3_I["Issuer 3"]
        A3_L["Listener 3"]
    end
    subgraph "Agent 2"
        A2_I["Issuer 2"]
        A2_L["Listener 2"]
    end
    subgraph "Agent 1"
        A1_I["Issuer 1"]
        A1_L["Listener 1"]
    end
    T["Tracker"]

    T --> O

    O --> A1_I
    O --> A2_I
    O --> A3_I

    A1_I --> A1_L
    A2_I --> A2_L
    A3_I --> A3_L

    A1_I --> T
    A2_I --> T
    A3_I --> T
    A1_L --> T
    A2_L --> T
    A3_L --> T
Issuer

The issuer is responsible for generating and issuing transactions. It notifies the tracker of all issued transactions.

Listener

The listener is responsible for listening to the network and confirming transactions or marking them as failed. As it receives transactions, it notifies the tracker of the transaction status.

Tracker

The tracker is responsible for maintaining metrics for all sent txs. Since the tracker is used by both the issuers, listeners and the orchestrator, all methods of the tracker must be thread safe.

Orchestrator

The orchestrator supports the following modes:

  • Gradual Load: the orchestrator sends transactions at an initial rate (TPS) and increases that rate until hitting the maxiumum desired rate or until the orchestrator determines that it can no longer make progress.
  • Burst: for each issuer, the orchestrator sends MinTPS transactions at once and waits for all transactions to be marked as accepted.

Setting MaxTPS in the orchestrator configuration to -1 sets the orchestrator in burst mode. Otherwise, the orchestator will run in gradual load mode.

The current TPS in the gradual orchestrator is determined by taking the number of transactions confirmed in a given time window (SustainedTime) and diving it by SustainedTime (in terms of seconds) Furthermore, the orchestator has MaxAttempts tries to try and achieve a given TPS before determining that the given TPS is not achievable.

Below is the pseudocode for how the orchestrator in gradual load mode determines TPS and for how it increases TPS:

currTargetTPS := current TPS we want to achieve
maxAttempts := maximum number of attempts we have to achieve currTargetTPS

txsPerIssuer := currTargetTPS / numOfIssuers
attempts := 0

for each issuer { // Async
    send txsPerIssuer txs per second
}

for {
    wait for SustainedTime

    tps := number of accepted txs divided by the SustainedTime
    if tps >= currTargetTPS:
        increase currTargerTPS by step
        iters = 0
    else:
        if attempts >= maxAttempts:
            fail
        iters += 1
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrFailedToReachTargetTPS = errors.New("failed to reach target TPS")

Functions

This section is empty.

Types

type Agent

type Agent[T TxID] struct {
	Issuer   Issuer[T]
	Listener Listener[T]
}

func NewAgent

func NewAgent[T TxID](
	issuer Issuer[T],
	listener Listener[T],
) Agent[T]

type Issuer

type Issuer[T TxID] interface {
	// GenerateAndIssueTx generates and sends a tx to the network, and informs the
	// tracker that it sent said transaction. It returns the sent transaction.
	GenerateAndIssueTx(ctx context.Context) (T, error)
}

type Listener

type Listener[T TxID] interface {
	// Listen for the final status of transactions and notify the tracker
	// Listen stops if the context is done, an error occurs, or if it received
	// all the transactions issued and the issuer no longer issues any.
	// Listen MUST return a nil error if the context is canceled.
	Listen(ctx context.Context) error

	// RegisterIssued informs the listener that a transaction was issued.
	RegisterIssued(txID T)

	// IssuingDone informs the listener that no more transactions will be issued.
	IssuingDone()
}

type Orchestrator

type Orchestrator[T TxID] struct {
	// contains filtered or unexported fields
}

Orchestrator tests the network by continuously sending transactions at a given rate (currTargetTPS) and increasing that rate until it detects that the network can no longer make progress (i.e. the rate at the network accepts transactions is less than currTargetTPS).

func NewOrchestrator

func NewOrchestrator[T TxID](
	agents []Agent[T],
	tracker *Tracker[T],
	log logging.Logger,
	config OrchestratorConfig,
) *Orchestrator[T]

func (*Orchestrator[_]) Execute

func (o *Orchestrator[_]) Execute(ctx context.Context) error

func (*Orchestrator[_]) GetMaxObservedTPS

func (o *Orchestrator[_]) GetMaxObservedTPS() int64

GetObservedIssued returns the max TPS the orchestrator observed

type OrchestratorConfig

type OrchestratorConfig struct {
	// The maximum TPS the orchestrator should aim for.
	//
	// If set to -1, the orchestrator will behave in a burst fashion, instead
	// sending MinTPS transactions at once and waiting sustainedTime seconds
	// before checking if MinTPS transactions were confirmed as accepted.
	MaxTPS int64
	// The minimum TPS the orchestrator should start with.
	MinTPS int64
	// The step size to increase the TPS by.
	Step int64

	// The factor by which to pad the number of txs an issuer sends per second
	// for example, if targetTPS = 1000 and numIssuers = 10, then each issuer
	// will send (1000/10)*TxRateMultiplier transactions per second.
	//
	// Maintaining a multiplier above target provides a toggle to keep load
	// persistently above instead of below target. This ensures load generation
	// does not pause issuance at the target and persistently under-issue and
	// fail to account for the time it takes to add more load.
	TxRateMultiplier float64

	// The time period which TPS is averaged over
	// Similarly, the time period which the orchestrator will wait before
	// computing the average TPS.
	SustainedTime time.Duration
	// The number of attempts to try achieving a given target TPS before giving up.
	MaxAttempts uint64

	// Whether the orchestrator should return if the maxTPS has been reached
	Terminate bool
}

func NewOrchestratorConfig

func NewOrchestratorConfig() OrchestratorConfig

NewOrchestratorConfig returns a default OrchestratorConfig with pre-set parameters for gradual load testing.

type Tracker

type Tracker[T TxID] struct {
	// contains filtered or unexported fields
}

Tracker keeps track of the status of transactions. This is thread-safe and can be called in parallel by the issuer(s) or orchestrator.

func NewTracker

func NewTracker[T TxID](reg *prometheus.Registry) (*Tracker[T], error)

NewTracker returns a new Tracker instance which records metrics for the number of transactions issued, confirmed, and failed. It also tracks the latency of transactions.

func (*Tracker[_]) GetObservedConfirmed

func (t *Tracker[_]) GetObservedConfirmed() uint64

GetObservedConfirmed returns the number of transactions that the tracker has confirmed were accepted.

func (*Tracker[_]) GetObservedFailed

func (t *Tracker[_]) GetObservedFailed() uint64

GetObservedFailed returns the number of transactions that the tracker has confirmed failed.

func (*Tracker[_]) GetObservedIssued

func (t *Tracker[_]) GetObservedIssued() uint64

GetObservedIssued returns the number of transactions that the tracker has confirmed were issued.

func (*Tracker[T]) Issue

func (t *Tracker[T]) Issue(txID T)

Issue records a transaction that was submitted, but whose final status is not yet known.

func (*Tracker[T]) ObserveConfirmed

func (t *Tracker[T]) ObserveConfirmed(txID T)

ObserveConfirmed records a transaction that was confirmed.

func (*Tracker[T]) ObserveFailed

func (t *Tracker[T]) ObserveFailed(txID T)

ObserveFailed records a transaction that failed (e.g. expired)

type TxID

type TxID interface {
	~[32]byte
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL