From f4dd7054c24dc22199cd37697087b29f56aa3869 Mon Sep 17 00:00:00 2001 From: agnusmor <100322135+agnusmor@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:52:34 +0100 Subject: [PATCH] Add gas per second estimation in metrics (#3401) * add gas estimation in metrics * fix linter --- sequencer/finalizer.go | 27 ++++++++++++----- sequencer/metrics.go | 66 +++++++++++++++++++++++++++--------------- 2 files changed, 61 insertions(+), 32 deletions(-) diff --git a/sequencer/finalizer.go b/sequencer/finalizer.go index 1c0e46489b..6336df3b79 100644 --- a/sequencer/finalizer.go +++ b/sequencer/finalizer.go @@ -483,12 +483,17 @@ func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, first // handleProcessTransactionResponse handles the response of transaction processing. func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *TxTracker, result *state.ProcessBatchResponse, oldStateRoot common.Hash) (errWg *sync.WaitGroup, err error) { + txResponse := result.BlockResponses[0].TransactionResponses[0] + + // Update metrics + f.wipL2Block.metrics.processedTxsCount++ + // Handle Transaction Error - errorCode := executor.RomErrorCode(result.BlockResponses[0].TransactionResponses[0].RomError) + errorCode := executor.RomErrorCode(txResponse.RomError) if !state.IsStateRootChanged(errorCode) { // If intrinsic error or OOC error, we skip adding the transaction to the batch errWg = f.handleProcessTransactionError(ctx, result, tx) - return errWg, result.BlockResponses[0].TransactionResponses[0].RomError + return errWg, txResponse.RomError } egpEnabled := f.effectiveGasPrice.IsEnabled() @@ -499,7 +504,7 @@ func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *Tx // Get the tx gas price we will use in the egp calculation. If egp is disabled we will use a "simulated" tx gas price txGasPrice, txL2GasPrice := f.effectiveGasPrice.GetTxAndL2GasPrice(tx.GasPrice, tx.L1GasPrice, tx.L2GasPrice) - newEffectiveGasPrice, err := f.effectiveGasPrice.CalculateEffectiveGasPrice(tx.RawTx, txGasPrice, result.BlockResponses[0].TransactionResponses[0].GasUsed, tx.L1GasPrice, txL2GasPrice) + newEffectiveGasPrice, err := f.effectiveGasPrice.CalculateEffectiveGasPrice(tx.RawTx, txGasPrice, txResponse.GasUsed, tx.L1GasPrice, txL2GasPrice) if err != nil { if egpEnabled { log.Errorf("failed to calculate effective gas price with new gasUsed for tx %s, error: %v", tx.HashStr, err.Error()) @@ -511,9 +516,9 @@ func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *Tx } else { // Save new (second) gas used and second effective gas price calculation for later logging tx.EGPLog.ValueSecond.Set(newEffectiveGasPrice) - tx.EGPLog.GasUsedSecond = result.BlockResponses[0].TransactionResponses[0].GasUsed + tx.EGPLog.GasUsedSecond = txResponse.GasUsed - errCompare := f.compareTxEffectiveGasPrice(ctx, tx, newEffectiveGasPrice, result.BlockResponses[0].TransactionResponses[0].HasGaspriceOpcode, result.BlockResponses[0].TransactionResponses[0].HasBalanceOpcode) + errCompare := f.compareTxEffectiveGasPrice(ctx, tx, newEffectiveGasPrice, txResponse.HasGaspriceOpcode, txResponse.HasBalanceOpcode) // If EffectiveGasPrice is disabled we will calculate the percentage and save it for later logging if !egpEnabled { @@ -572,14 +577,14 @@ func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *Tx // If reserved tx resources don't fit in the remaining batch resources (or we got an overflow when trying to subtract the used resources) // we update the ZKCounters of the tx and returns ErrBatchResourceOverFlow error if !fits || subOverflow { - f.workerIntf.UpdateTxZKCounters(result.BlockResponses[0].TransactionResponses[0].TxHash, tx.From, result.UsedZkCounters, result.ReservedZkCounters) + f.workerIntf.UpdateTxZKCounters(txResponse.TxHash, tx.From, result.UsedZkCounters, result.ReservedZkCounters) return nil, ErrBatchResourceOverFlow } // Save Enabled, GasPriceOC, BalanceOC and final effective gas price for later logging tx.EGPLog.Enabled = egpEnabled - tx.EGPLog.GasPriceOC = result.BlockResponses[0].TransactionResponses[0].HasGaspriceOpcode - tx.EGPLog.BalanceOC = result.BlockResponses[0].TransactionResponses[0].HasBalanceOpcode + tx.EGPLog.GasPriceOC = txResponse.HasGaspriceOpcode + tx.EGPLog.BalanceOC = txResponse.HasBalanceOpcode tx.EGPLog.ValueFinal.Set(tx.EffectiveGasPrice) // Log here the results of EGP calculation @@ -593,6 +598,9 @@ func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *Tx f.updateWorkerAfterSuccessfulProcessing(ctx, tx.Hash, tx.From, false, result) + // Update metrics + f.wipL2Block.metrics.gas += txResponse.GasUsed + return nil, nil } @@ -720,6 +728,9 @@ func (f *finalizer) handleProcessTransactionError(ctx context.Context, result *s }() } + // Update metrics + f.wipL2Block.metrics.gas += txResponse.GasUsed + return wg } diff --git a/sequencer/metrics.go b/sequencer/metrics.go index ea39caa9ac..2be977e23e 100644 --- a/sequencer/metrics.go +++ b/sequencer/metrics.go @@ -2,6 +2,7 @@ package sequencer import ( "fmt" + "math" "time" ) @@ -34,28 +35,35 @@ func (p *processTimes) sumUp(ptSumUp processTimes) { type metrics struct { closedAt time.Time - txsCount int64 + processedTxsCount int64 + l2BlockTxsCount int64 idleTime time.Duration newL2BlockTimes processTimes transactionsTimes processTimes l2BlockTimes processTimes + gas uint64 estimatedTxsPerSec float64 + estimatedGasPerSec uint64 } func (m *metrics) sub(mSub metrics) { - m.txsCount -= mSub.txsCount + m.processedTxsCount -= mSub.processedTxsCount + m.l2BlockTxsCount -= mSub.l2BlockTxsCount m.idleTime -= mSub.idleTime m.newL2BlockTimes.sub(mSub.newL2BlockTimes) m.transactionsTimes.sub(mSub.transactionsTimes) m.l2BlockTimes.sub(mSub.l2BlockTimes) + m.gas -= mSub.gas } func (m *metrics) sumUp(mSumUp metrics) { - m.txsCount += mSumUp.txsCount + m.processedTxsCount += mSumUp.processedTxsCount + m.l2BlockTxsCount += mSumUp.l2BlockTxsCount m.idleTime += mSumUp.idleTime m.newL2BlockTimes.sumUp(mSumUp.newL2BlockTimes) m.transactionsTimes.sumUp(mSumUp.transactionsTimes) m.l2BlockTimes.sumUp(mSumUp.l2BlockTimes) + m.gas += mSumUp.gas } func (m *metrics) executorTime() time.Duration { @@ -70,27 +78,32 @@ func (m *metrics) totalTime() time.Duration { return m.newL2BlockTimes.total() + m.transactionsTimes.total() + m.l2BlockTimes.total() + m.idleTime } -func (m *metrics) close(createdAt time.Time, txsCount int64) { +func (m *metrics) close(createdAt time.Time, l2BlockTxsCount int64) { // Compute pending fields m.closedAt = time.Now() totalTime := time.Since(createdAt) - m.txsCount = txsCount + m.l2BlockTxsCount = l2BlockTxsCount m.transactionsTimes.sequencer = totalTime - m.idleTime - m.newL2BlockTimes.total() - m.transactionsTimes.executor - m.l2BlockTimes.total() // Compute performance - if m.txsCount > 0 { + if m.processedTxsCount > 0 { // timePerTxuS is the average time spent per tx. This includes the l2Block time since the processing time of this section is proportional to the number of txs - timePerTxuS := (m.transactionsTimes.total() + m.l2BlockTimes.total()).Microseconds() / m.txsCount + timePerTxuS := (m.transactionsTimes.total() + m.l2BlockTimes.total()).Microseconds() / m.processedTxsCount // estimatedTxs is the number of transactions that we estimate could have been processed in the block estimatedTxs := float64(totalTime.Microseconds()-m.newL2BlockTimes.total().Microseconds()) / float64(timePerTxuS) - // estimatedTxxPerSec is the estimated transactions per second - m.estimatedTxsPerSec = estimatedTxs / totalTime.Seconds() + // estimatedTxxPerSec is the estimated transactions per second (rounded to 2 decimal digits) + m.estimatedTxsPerSec = math.Ceil(estimatedTxs/totalTime.Seconds()*100) / 100 //nolint:gomnd + + // gasPerTx is the average gas used per tx + gasPerTx := m.gas / uint64(m.processedTxsCount) + // estimatedGasPerSec is the estimated gas per second + m.estimatedGasPerSec = uint64(m.estimatedTxsPerSec * float64(gasPerTx)) } } func (m *metrics) log() string { - return fmt.Sprintf("txs: %d, estimated txs/s: %.1f, time: {total: %d, idle: %d, sequencer: {total: %d, newL2Block: %d, txs: %d, l2Block: %d}, executor: {total: %d, newL2Block: %d, txs: %d, l2Block: %d}", - m.txsCount, m.estimatedTxsPerSec, m.totalTime().Microseconds(), m.idleTime.Microseconds(), + return fmt.Sprintf("blockTxs: %d, txs: %d, gas: %d, txsSec: %.2f, gasSec: %d, time: {total: %d, idle: %d, sequencer: {total: %d, newL2Block: %d, txs: %d, l2Block: %d}, executor: {total: %d, newL2Block: %d, txs: %d, l2Block: %d}", + m.l2BlockTxsCount, m.processedTxsCount, m.gas, m.estimatedTxsPerSec, m.estimatedGasPerSec, m.totalTime().Microseconds(), m.idleTime.Microseconds(), m.sequencerTime().Microseconds(), m.newL2BlockTimes.sequencer.Microseconds(), m.transactionsTimes.sequencer.Microseconds(), m.l2BlockTimes.sequencer.Microseconds(), m.executorTime().Microseconds(), m.newL2BlockTimes.executor.Microseconds(), m.transactionsTimes.executor.Microseconds(), m.l2BlockTimes.executor.Microseconds()) } @@ -99,8 +112,9 @@ type intervalMetrics struct { l2Blocks []*metrics maxInterval time.Duration metrics - estimatedTxsPerSecAcc float64 - estimatedTxsPerSecCount int64 + estimatedTxsPerSecAcc float64 + estimatedGasPerSecAcc uint64 + l2BlockCountAcc int64 } func newIntervalMetrics(maxInterval time.Duration) *intervalMetrics { @@ -122,9 +136,10 @@ func (i *intervalMetrics) cleanUp() { if l2Block.closedAt.Add(i.maxInterval).Before(now) { // Subtract l2Block metrics from accumulated values i.sub(*l2Block) - if l2Block.txsCount > 0 { - i.estimatedTxsPerSecAcc -= i.estimatedTxsPerSec - i.estimatedTxsPerSecCount-- + if l2Block.processedTxsCount > 0 { + i.estimatedTxsPerSecAcc -= l2Block.estimatedTxsPerSec + i.estimatedGasPerSecAcc -= l2Block.estimatedGasPerSec + i.l2BlockCountAcc-- } // Remove from l2Blocks i.l2Blocks = i.l2Blocks[1:] @@ -136,7 +151,7 @@ func (i *intervalMetrics) cleanUp() { if ct > 0 { // Compute performance - i.computeEstimatedTxsPerSec() + i.computePerformance() } } @@ -144,20 +159,23 @@ func (i *intervalMetrics) addL2BlockMetrics(l2Block metrics) { i.cleanUp() i.sumUp(l2Block) - if l2Block.txsCount > 0 { + if l2Block.processedTxsCount > 0 { i.estimatedTxsPerSecAcc += l2Block.estimatedTxsPerSec - i.estimatedTxsPerSecCount++ - i.computeEstimatedTxsPerSec() + i.estimatedGasPerSecAcc += l2Block.estimatedGasPerSec + i.l2BlockCountAcc++ + i.computePerformance() } i.l2Blocks = append(i.l2Blocks, &l2Block) } -func (i *intervalMetrics) computeEstimatedTxsPerSec() { - if i.estimatedTxsPerSecCount > 0 { - i.estimatedTxsPerSec = i.estimatedTxsPerSecAcc / float64(i.estimatedTxsPerSecCount) +func (i *intervalMetrics) computePerformance() { + if i.l2BlockCountAcc > 0 { + i.estimatedTxsPerSec = math.Ceil(i.estimatedTxsPerSecAcc/float64(i.l2BlockCountAcc)*100) / 100 //nolint:gomnd + i.estimatedGasPerSec = i.estimatedGasPerSecAcc / uint64(i.l2BlockCountAcc) } else { - i.estimatedTxsPerSecCount = 0 + i.estimatedTxsPerSec = 0 + i.estimatedGasPerSec = 0 } }