Documentation
¶
Overview ¶
Package txmon follows the transactions of load generators through the network and derives metrics from where they are and how long they took to get there.
A transaction becomes observable at three moments, each seen through a different window:
- submitted: it was handed to the RPC interface of a node, which either accepted it into its transaction pool or refused it. The network reports this moment itself, see driver.TransactionObserver.
- emitted: a validator put it into an event of the DAG, which is when it starts travelling towards a block. Found by walking the DAG, see emissions.go.
- included: it became part of a block and was therefore executed. Read from the transactions of every new block, see inclusions.go.
Between submission and emission a transaction waits in a pool, where it is either emittable or parked behind a missing nonce of its own sender. The two are counted apart, in the same way a transaction pool separates its pending from its queued transactions: a growing number of emittable transactions means the network does not keep up with the offered load, while parked ones will not be emitted at all until the gap before them is filled - usually because a submission was lost. The distinction is derived from the nonces of the observed transactions rather than read from the pools, which would mean transferring their entire contents on every sample.
Only transactions of load generators are followed. The transactions Norma sends to set a scenario up - funding accounts, deploying contracts, changing rules - do not pass through the observed submission path.
Index ¶
- Constants
- Variables
- type BlockContribution
- type BlockLimit
- type CountKind
- type Counts
- type IncludedTransaction
- type Sample
- type SampleKind
- type Tracker
- func (t *Tracker) Apps() []string
- func (t *Tracker) BlockContributions(app string) []BlockContribution
- func (t *Tracker) BlockGasLimits() []BlockLimit
- func (t *Tracker) Contributors() []string
- func (t *Tracker) Counts(app string) Counts
- func (t *Tracker) LogSummary()
- func (t *Tracker) MarkBlock(height int, at time.Time, txs []IncludedTransaction)
- func (t *Tracker) MarkBlockLimits(height int, limits BlockLimit)
- func (t *Tracker) MarkEmitted(hash common.Hash, at time.Time)
- func (t *Tracker) MarkIncluded(hash common.Hash, at time.Time)
- func (t *Tracker) OnTransactionSubmitted(source driver.TransactionSource, tx *types.Transaction, at time.Time, ...)
- func (t *Tracker) Samples(app string, kind SampleKind) []Sample
Constants ¶
const OtherTransactions = "(other)"
OtherTransactions is the name the composition of a block attributes the transactions of no application to. It is not a valid application name, so it cannot collide with one.
Variables ¶
var ( TransactionsPending = mon.Metric[mon.App, mon.Series[mon.Time, int]]{ Name: "TransactionsPending", Description: "The number of submitted transactions the network could emit right away, waiting for capacity", } TransactionsStalled = mon.Metric[mon.App, mon.Series[mon.Time, int]]{ Name: "TransactionsStalled", Description: "The number of submitted transactions stalling in a pool behind a missing nonce of their own sender", } TransactionsEmitted = mon.Metric[mon.App, mon.Series[mon.Time, int]]{ Name: "TransactionsEmitted", Description: "The number of transactions carried by an event of the DAG but not yet by a block", } TransactionsIncluded = mon.Metric[mon.App, mon.Series[mon.Time, int]]{ Name: "TransactionsIncluded", Description: "The total number of transactions that became part of a block", } TransactionsRejected = mon.Metric[mon.App, mon.Series[mon.Time, int]]{ Name: "TransactionsRejected", Description: "The total number of transactions a node refused to accept", } )
The number of transactions of an application in each phase of their life, sampled once per second. The first three are disjoint and add up to the transactions of the application currently in the system.
var ( BlockTransactionsPerApp = mon.Metric[mon.App, mon.Series[mon.BlockNumber, int]]{ Name: "BlockTransactionsPerApp", Description: "The number of transactions of an application in a block, the transactions of none of them under " + OtherTransactions, } BlockGasPerApp = mon.Metric[mon.App, mon.Series[mon.BlockNumber, int]]{ Name: "BlockGasPerApp", Description: "The gas used in a block by the transactions of an application, the transactions of none of them under " + OtherTransactions, } )
How the applications shared each block, counted in transactions and in the gas they used. Per block the values add up to that block's own totals.
var ( BlockGasLimit = mon.Metric[mon.Network, mon.Series[mon.BlockNumber, int]]{ Name: "BlockGasLimit", Description: "The gas limit a block was formed under, as reported by its header", } EventGasLimit = mon.Metric[mon.Network, mon.Series[mon.BlockNumber, int]]{ Name: "EventGasLimit", Description: "The gas one event carrying the transactions of a block may hold, the MaxEventGas rule", } GasPowerAllocPerSec = mon.Metric[mon.Network, mon.Series[mon.BlockNumber, int]]{ Name: "GasPowerAllocPerSec", Description: "The gas per second the validators of the network may spend together, the ShortGasPower.AllocPerSec rule", } )
The gas ceilings that applied to a block, recorded per block so they can be read next to the gas the block used. BlockGasLimit is the technical hard limit of the block itself, which a network is not normally expected to reach; what a block actually carries is governed by the other two.
var ( TransactionTimeToEmit = mon.Metric[mon.App, mon.Series[mon.Time, time.Duration]]{ Name: "TransactionTimeToEmit", Description: "The time transactions spent between their submission and being emitted in an event", } TransactionTimeToInclude = mon.Metric[mon.App, mon.Series[mon.Time, time.Duration]]{ Name: "TransactionTimeToInclude", Description: "The time transactions spent between their submission and becoming part of a block", } TransactionTimeEmitToInclude = mon.Metric[mon.App, mon.Series[mon.Time, time.Duration]]{ Name: "TransactionTimeEmitToInclude", Description: "The time emitted transactions needed to reach a block", } )
The durations transactions needed to pass through the phases of their life. Each data point is one transaction, positioned at the moment it was submitted; there are far too many of them to read individually, they describe a distribution to be aggregated.
Functions ¶
This section is empty.
Types ¶
type BlockContribution ¶
type BlockContribution struct {
Block int
Transactions int
// Gas is the gas the transactions actually used, not the gas they reserved,
// so the contributions of a block add up to the gas used by that block.
Gas uint64
}
BlockContribution is what one application contributed to one block.
type BlockLimit ¶
type BlockLimit struct {
Block int
GasLimit uint64 // Blocks.MaxBlockGas, from the block header
EventGasLimit uint64 // Economy.Gas.MaxEventGas
GasPowerPerSec uint64 // Economy.ShortGasPower.AllocPerSec
}
BlockLimit are the gas ceilings that applied to one block: what the block itself may hold, what one event carrying its transactions may hold, and the gas per second the validators were allowed to spend.
type CountKind ¶
type CountKind int
CountKind identifies one of the populations of transactions counted per application. The first three are disjoint and count the transactions currently in the system; the last two accumulate over the run.
const ( // Pending counts submitted transactions that could be emitted right away: // they form an uninterrupted run of nonces from the one their sender // executes next. Pending CountKind = iota // Stalled counts submitted transactions parked behind a missing nonce of // their own sender, which no validator can emit until that gap is filled. Stalled // Emitted counts transactions carried by an event but not yet by a block. Emitted // Included counts transactions that made it into a block, over the whole run. Included // Rejected counts transactions a node refused to accept, over the whole run. Rejected )
type IncludedTransaction ¶
IncludedTransaction is a transaction of a block, as read from its receipt.
type Sample ¶
Sample is one measured duration of one transaction, positioned at the moment the transaction was submitted.
type SampleKind ¶
type SampleKind int
SampleKind identifies one of the durations measured per transaction.
const ( // TimeToEmit is the time from the submission of a transaction until a // validator put it into an event. TimeToEmit SampleKind = iota // TimeToInclude is the time from the submission of a transaction until it // became part of a block - the latency a user of the network observes. TimeToInclude // TimeEmitToInclude is the time an emitted transaction needed to reach a // block, which is the part of the latency consensus is responsible for. TimeEmitToInclude )
type Tracker ¶
type Tracker struct {
// contains filtered or unexported fields
}
Tracker maintains the state of the transactions submitted to a network. It is safe for concurrent use: submissions, emissions and inclusions are reported from independent goroutines.
func (*Tracker) BlockContributions ¶
func (t *Tracker) BlockContributions(app string) []BlockContribution
BlockContributions returns what the given application contributed to each block, ordered by block height. Use OtherTransactions for the transactions that belong to no application.
func (*Tracker) BlockGasLimits ¶
func (t *Tracker) BlockGasLimits() []BlockLimit
BlockGasLimits returns the gas ceilings of every observed block, ordered by block height.
func (*Tracker) Contributors ¶
Contributors returns the names of everything that contributed transactions to a block, which are the applications plus possibly OtherTransactions.
func (*Tracker) LogSummary ¶
func (t *Tracker) LogSummary()
LogSummary reports what the tracker had to leave out, all of which would silently distort the metrics derived from it.
func (*Tracker) MarkBlock ¶
func (t *Tracker) MarkBlock(height int, at time.Time, txs []IncludedTransaction)
MarkBlock reports the transactions of a block observed at the given time. It records which of them belonged to which application, which is the composition of that block, and marks them as included.
func (*Tracker) MarkBlockLimits ¶
func (t *Tracker) MarkBlockLimits(height int, limits BlockLimit)
MarkBlockLimits reports the gas ceilings that applied to the given block. They are recorded for every observed block, including the ones that carried no transaction, so that the limits can be read next to what the blocks used.
func (*Tracker) MarkEmitted ¶
MarkEmitted reports that a transaction was seen in an event created at the given time. Reports for unknown transactions, and repeated reports for the same one, are ignored.
func (*Tracker) MarkIncluded ¶
MarkIncluded reports that a transaction became part of a block observed at the given time. Reports for unknown transactions are ignored.
func (*Tracker) OnTransactionSubmitted ¶
func (t *Tracker) OnTransactionSubmitted( source driver.TransactionSource, tx *types.Transaction, at time.Time, err error, )
OnTransactionSubmitted implements driver.TransactionObserver.