diff --git a/.changeset/lovely-shrimps-explain.md b/.changeset/lovely-shrimps-explain.md new file mode 100644 index 0000000000000..80dd77539b12b --- /dev/null +++ b/.changeset/lovely-shrimps-explain.md @@ -0,0 +1,6 @@ +--- +'@eth-optimism/batch-submitter-service': patch +--- + +Refactors the bss-core service to use a metrics interface to allow +driver-specific metric extensions diff --git a/go/batch-submitter/drivers/proposer/driver.go b/go/batch-submitter/drivers/proposer/driver.go index fb3fdfab31cdf..1ba44a68f43e1 100644 --- a/go/batch-submitter/drivers/proposer/driver.go +++ b/go/batch-submitter/drivers/proposer/driver.go @@ -45,7 +45,7 @@ type Driver struct { rawSccContract *bind.BoundContract ctcContract *ctc.CanonicalTransactionChain walletAddr common.Address - metrics *metrics.Metrics + metrics *metrics.Base } func NewDriver(cfg Config) (*Driver, error) { @@ -82,7 +82,7 @@ func NewDriver(cfg Config) (*Driver, error) { rawSccContract: rawSccContract, ctcContract: ctcContract, walletAddr: walletAddr, - metrics: metrics.NewMetrics(cfg.Name), + metrics: metrics.NewBase("batch_submitter", cfg.Name), }, nil } @@ -97,7 +97,7 @@ func (d *Driver) WalletAddr() common.Address { } // Metrics returns the subservice telemetry object. -func (d *Driver) Metrics() *metrics.Metrics { +func (d *Driver) Metrics() metrics.Metrics { return d.metrics } @@ -184,7 +184,7 @@ func (d *Driver) CraftBatchTx( stateRoots = append(stateRoots, block.Root()) } - d.metrics.NumElementsPerBatch.Observe(float64(len(stateRoots))) + d.metrics.NumElementsPerBatch().Observe(float64(len(stateRoots))) log.Info(name+" batch constructed", "num_state_roots", len(stateRoots)) diff --git a/go/batch-submitter/drivers/sequencer/driver.go b/go/batch-submitter/drivers/sequencer/driver.go index 7b8cb0b64a209..1205fc88b5784 100644 --- a/go/batch-submitter/drivers/sequencer/driver.go +++ b/go/batch-submitter/drivers/sequencer/driver.go @@ -44,7 +44,7 @@ type Driver struct { rawCtcContract *bind.BoundContract walletAddr common.Address ctcABI *abi.ABI - metrics *metrics.Metrics + metrics *Metrics } func NewDriver(cfg Config) (*Driver, error) { @@ -80,7 +80,7 @@ func NewDriver(cfg Config) (*Driver, error) { rawCtcContract: rawCtcContract, walletAddr: walletAddr, ctcABI: ctcABI, - metrics: metrics.NewMetrics(cfg.Name), + metrics: NewMetrics(cfg.Name), }, nil } @@ -95,7 +95,7 @@ func (d *Driver) WalletAddr() common.Address { } // Metrics returns the subservice telemetry object. -func (d *Driver) Metrics() *metrics.Metrics { +func (d *Driver) Metrics() metrics.Metrics { return d.metrics } @@ -219,7 +219,7 @@ func (d *Driver) CraftBatchTx( continue } - d.metrics.NumElementsPerBatch.Observe(float64(len(batchElements))) + d.metrics.NumElementsPerBatch().Observe(float64(len(batchElements))) d.metrics.BatchPruneCount.Set(float64(pruneCount)) log.Info(name+" batch constructed", "num_txs", len(batchElements), "length", len(batchCallData)) diff --git a/go/batch-submitter/drivers/sequencer/metrics.go b/go/batch-submitter/drivers/sequencer/metrics.go new file mode 100644 index 0000000000000..b5b0526df996d --- /dev/null +++ b/go/batch-submitter/drivers/sequencer/metrics.go @@ -0,0 +1,30 @@ +package sequencer + +import ( + "github.com/ethereum-optimism/optimism/go/bss-core/metrics" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +// Metrics extends the BSS core metrics with additional metrics tracked by the +// sequencer driver. +type Metrics struct { + *metrics.Base + + // BatchPruneCount tracks the number of times a batch of sequencer + // transactions is pruned in order to meet the desired size requirements. + BatchPruneCount prometheus.Gauge +} + +// NewMetrics initializes a new, extended metrics object. +func NewMetrics(subsystem string) *Metrics { + base := metrics.NewBase("batch_submitter", subsystem) + return &Metrics{ + Base: base, + BatchPruneCount: promauto.NewGauge(prometheus.GaugeOpts{ + Name: "batch_prune_count", + Help: "Number of times a batch is pruned", + Subsystem: base.SubsystemName(), + }), + } +} diff --git a/go/batch-submitter/go.mod b/go/batch-submitter/go.mod index df2f5651ff6c1..f853498dd1721 100644 --- a/go/batch-submitter/go.mod +++ b/go/batch-submitter/go.mod @@ -7,6 +7,7 @@ require ( github.com/ethereum-optimism/optimism/l2geth v1.0.0 github.com/ethereum/go-ethereum v1.10.16 github.com/getsentry/sentry-go v0.11.0 + github.com/prometheus/client_golang v1.11.0 github.com/stretchr/testify v1.7.0 github.com/urfave/cli v1.22.5 ) diff --git a/go/bss-core/metrics/interface.go b/go/bss-core/metrics/interface.go new file mode 100644 index 0000000000000..6644afbe0b207 --- /dev/null +++ b/go/bss-core/metrics/interface.go @@ -0,0 +1,38 @@ +package metrics + +import "github.com/prometheus/client_golang/prometheus" + +type Metrics interface { + // SubsystemName returns the subsystem name for the metrics group. + SubsystemName() string + + // BalanceETH tracks the amount of ETH in the submitter's account. + BalanceETH() prometheus.Gauge + + // BatchSizeBytes tracks the size of batch submission transactions. + BatchSizeBytes() prometheus.Summary + + // NumElementsPerBatch tracks the number of L2 transactions in each batch + // submission. + NumElementsPerBatch() prometheus.Summary + + // SubmissionTimestamp tracks the time at which each batch was confirmed. + SubmissionTimestamp() prometheus.Gauge + + // SubmissionGasUsedWei tracks the amount of gas used to submit each batch. + SubmissionGasUsedWei() prometheus.Gauge + + // BatchsSubmitted tracks the total number of successful batch submissions. + BatchesSubmitted() prometheus.Counter + + // FailedSubmissions tracks the total number of failed batch submissions. + FailedSubmissions() prometheus.Counter + + // BatchTxBuildTimeMs tracks the duration it takes to construct a batch + // transaction. + BatchTxBuildTimeMs() prometheus.Gauge + + // BatchConfirmationTimeMs tracks the duration it takes to confirm a batch + // transaction. + BatchConfirmationTimeMs() prometheus.Gauge +} diff --git a/go/bss-core/metrics/metrics.go b/go/bss-core/metrics/metrics.go index 9daa4783d3fa1..38a6e4bcf2135 100644 --- a/go/bss-core/metrics/metrics.go +++ b/go/bss-core/metrics/metrics.go @@ -1,104 +1,178 @@ package metrics import ( + "fmt" "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ) -type Metrics struct { - // ETHBalance tracks the amount of ETH in the submitter's account. - ETHBalance prometheus.Gauge +type Base struct { + // subsystemName stores the name that will prefix all metrics. This can be + // used by drivers to futher extend the core metrics and ensure they use the + // same prefix. + subsystemName string - // BatchSizeInBytes tracks the size of batch submission transactions. - BatchSizeInBytes prometheus.Summary + // balanceETH tracks the amount of ETH in the submitter's account. + balanceETH prometheus.Gauge - // NumElementsPerBatch tracks the number of L2 transactions in each batch + // batchSizeBytes tracks the size of batch submission transactions. + batchSizeBytes prometheus.Summary + + // numElementsPerBatch tracks the number of L2 transactions in each batch // submission. - NumElementsPerBatch prometheus.Summary + numElementsPerBatch prometheus.Summary - // SubmissionTimestamp tracks the time at which each batch was confirmed. - SubmissionTimestamp prometheus.Gauge + // submissionTimestamp tracks the time at which each batch was confirmed. + submissionTimestamp prometheus.Gauge - // SubmissionGasUsed tracks the amount of gas used to submit each batch. - SubmissionGasUsed prometheus.Gauge + // submissionGasUsedWei tracks the amount of gas used to submit each batch. + submissionGasUsedWei prometheus.Gauge - // BatchsSubmitted tracks the total number of successful batch submissions. - BatchesSubmitted prometheus.Counter + // batchsSubmitted tracks the total number of successful batch submissions. + batchesSubmitted prometheus.Counter - // FailedSubmissions tracks the total number of failed batch submissions. - FailedSubmissions prometheus.Counter + // failedSubmissions tracks the total number of failed batch submissions. + failedSubmissions prometheus.Counter - // BatchTxBuildTime tracks the duration it takes to construct a batch + // batchTxBuildTimeMs tracks the duration it takes to construct a batch // transaction. - BatchTxBuildTime prometheus.Gauge + batchTxBuildTimeMs prometheus.Gauge - // BatchConfirmationTime tracks the duration it takes to confirm a batch + // batchConfirmationTimeMs tracks the duration it takes to confirm a batch // transaction. - BatchConfirmationTime prometheus.Gauge - - // BatchPruneCount tracks the number of times a batch of sequencer - // transactions is pruned in order to meet the desired size requirements. - // - // NOTE: This is currently only active in the sequencer driver. - BatchPruneCount prometheus.Gauge + batchConfirmationTimeMs prometheus.Gauge } -func NewMetrics(subsystem string) *Metrics { - subsystem = "batch_submitter_" + strings.ToLower(subsystem) - return &Metrics{ - ETHBalance: promauto.NewGauge(prometheus.GaugeOpts{ +func NewBase(serviceName, subServiceName string) *Base { + subsystem := MakeSubsystemName(serviceName, subServiceName) + return &Base{ + subsystemName: subsystem, + balanceETH: promauto.NewGauge(prometheus.GaugeOpts{ Name: "balance_eth", Help: "ETH balance of the batch submitter", Subsystem: subsystem, }), - BatchSizeInBytes: promauto.NewSummary(prometheus.SummaryOpts{ + batchSizeBytes: promauto.NewSummary(prometheus.SummaryOpts{ Name: "batch_size_bytes", Help: "Size of batches in bytes", Subsystem: subsystem, Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }), - NumElementsPerBatch: promauto.NewSummary(prometheus.SummaryOpts{ + numElementsPerBatch: promauto.NewSummary(prometheus.SummaryOpts{ Name: "num_elements_per_batch", Help: "Number of elements in each batch", Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, Subsystem: subsystem, }), - SubmissionTimestamp: promauto.NewGauge(prometheus.GaugeOpts{ + submissionTimestamp: promauto.NewGauge(prometheus.GaugeOpts{ Name: "submission_timestamp_ms", Help: "Timestamp of last batch submitter submission", Subsystem: subsystem, }), - SubmissionGasUsed: promauto.NewGauge(prometheus.GaugeOpts{ + submissionGasUsedWei: promauto.NewGauge(prometheus.GaugeOpts{ Name: "submission_gas_used_wei", Help: "Gas used to submit each batch", Subsystem: subsystem, }), - BatchesSubmitted: promauto.NewCounter(prometheus.CounterOpts{ + batchesSubmitted: promauto.NewCounter(prometheus.CounterOpts{ Name: "batches_submitted", Help: "Count of batches submitted", Subsystem: subsystem, }), - FailedSubmissions: promauto.NewCounter(prometheus.CounterOpts{ + failedSubmissions: promauto.NewCounter(prometheus.CounterOpts{ Name: "failed_submissions", Help: "Count of failed batch submissions", Subsystem: subsystem, }), - BatchTxBuildTime: promauto.NewGauge(prometheus.GaugeOpts{ + batchTxBuildTimeMs: promauto.NewGauge(prometheus.GaugeOpts{ Name: "batch_tx_build_time_ms", Help: "Time to construct batch transactions", Subsystem: subsystem, }), - BatchConfirmationTime: promauto.NewGauge(prometheus.GaugeOpts{ + batchConfirmationTimeMs: promauto.NewGauge(prometheus.GaugeOpts{ Name: "batch_confirmation_time_ms", Help: "Time to confirm batch transactions", Subsystem: subsystem, }), - BatchPruneCount: promauto.NewGauge(prometheus.GaugeOpts{ - Name: "batch_prune_count", - Help: "Number of times a batch is pruned", - Subsystem: subsystem, - }), } } + +// SubsystemName returns the subsystem name for the metrics group. +func (b *Base) SubsystemName() string { + return b.subsystemName +} + +// BalanceETH tracks the amount of ETH in the submitter's account. +func (b *Base) BalanceETH() prometheus.Gauge { + return b.balanceETH +} + +// BatchSizeBytes tracks the size of batch submission transactions. +func (b *Base) BatchSizeBytes() prometheus.Summary { + return b.batchSizeBytes +} + +// NumElementsPerBatch tracks the number of L2 transactions in each batch +// submission. +func (b *Base) NumElementsPerBatch() prometheus.Summary { + return b.numElementsPerBatch +} + +// SubmissionTimestamp tracks the time at which each batch was confirmed. +func (b *Base) SubmissionTimestamp() prometheus.Gauge { + return b.submissionTimestamp +} + +// SubmissionGasUsedWei tracks the amount of gas used to submit each batch. +func (b *Base) SubmissionGasUsedWei() prometheus.Gauge { + return b.submissionGasUsedWei +} + +// BatchsSubmitted tracks the total number of successful batch submissions. +func (b *Base) BatchesSubmitted() prometheus.Counter { + return b.batchesSubmitted +} + +// FailedSubmissions tracks the total number of failed batch submissions. +func (b *Base) FailedSubmissions() prometheus.Counter { + return b.failedSubmissions +} + +// BatchTxBuildTimeMs tracks the duration it takes to construct a batch +// transaction. +func (b *Base) BatchTxBuildTimeMs() prometheus.Gauge { + return b.batchTxBuildTimeMs +} + +// BatchConfirmationTimeMs tracks the duration it takes to confirm a batch +// transaction. +func (b *Base) BatchConfirmationTimeMs() prometheus.Gauge { + return b.batchConfirmationTimeMs +} + +// MakeSubsystemName builds the subsystem name for a group of metrics, which +// prometheus will use to prefix all metrics in the group. If two non-empty +// strings are provided, they are joined with an underscore. If only one +// non-empty string is provided, that name will be used alone. Otherwise an +// empty string is returned after converting the characters to lower case. +// +// NOTE: This method panics if spaces are included in either string. +func MakeSubsystemName(serviceName string, subServiceName string) string { + var subsystem string + switch { + case serviceName != "" && subServiceName != "": + subsystem = fmt.Sprintf("%s_%s", serviceName, subServiceName) + case serviceName != "": + subsystem = serviceName + default: + subsystem = subServiceName + } + + if strings.ContainsAny(subsystem, " ") { + panic(fmt.Sprintf("metrics name \"%s\"cannot have spaces", subsystem)) + } + + return strings.ToLower(subsystem) +} diff --git a/go/bss-core/service.go b/go/bss-core/service.go index cab832e00150a..4a4ebbc7f4405 100644 --- a/go/bss-core/service.go +++ b/go/bss-core/service.go @@ -31,7 +31,7 @@ type Driver interface { WalletAddr() common.Address // Metrics returns the subservice telemetry object. - Metrics() *metrics.Metrics + Metrics() metrics.Metrics // ClearPendingTx a publishes a transaction at the next available nonce in // order to clear any transactions in the mempool left over from a prior @@ -83,7 +83,7 @@ type Service struct { cancel func() txMgr txmgr.TxManager - metrics *metrics.Metrics + metrics metrics.Metrics wg sync.WaitGroup } @@ -147,7 +147,7 @@ func (s *Service) eventLoop() { log.Error(name+" unable to get current balance", "err", err) continue } - s.metrics.ETHBalance.Set(weiToEth64(balance)) + s.metrics.BalanceETH().Set(weiToEth64(balance)) // Determine the range of L2 blocks that the batch submitter has not // processed, and needs to take action on. @@ -186,7 +186,7 @@ func (s *Service) eventLoop() { continue } batchTxBuildTime := time.Since(batchTxBuildStart) / time.Millisecond - s.metrics.BatchTxBuildTime.Set(float64(batchTxBuildTime)) + s.metrics.BatchTxBuildTimeMs().Set(float64(batchTxBuildTime)) // Record the size of the batch transaction. var txBuf bytes.Buffer @@ -194,7 +194,7 @@ func (s *Service) eventLoop() { log.Error(name+" unable to encode batch tx", "err", err) continue } - s.metrics.BatchSizeInBytes.Observe(float64(len(txBuf.Bytes()))) + s.metrics.BatchSizeBytes().Observe(float64(len(txBuf.Bytes()))) // Construct the transaction submission clousure that will attempt // to send the next transaction at the given nonce and gas price. @@ -214,7 +214,7 @@ func (s *Service) eventLoop() { if err != nil { log.Error(name+" unable to publish batch tx", "err", err) - s.metrics.FailedSubmissions.Inc() + s.metrics.FailedSubmissions().Inc() continue } @@ -223,10 +223,10 @@ func (s *Service) eventLoop() { "tx_hash", receipt.TxHash) batchConfirmationTime := time.Since(batchConfirmationStart) / time.Millisecond - s.metrics.BatchConfirmationTime.Set(float64(batchConfirmationTime)) - s.metrics.BatchesSubmitted.Inc() - s.metrics.SubmissionGasUsed.Set(float64(receipt.GasUsed)) - s.metrics.SubmissionTimestamp.Set(float64(time.Now().UnixNano() / 1e6)) + s.metrics.BatchConfirmationTimeMs().Set(float64(batchConfirmationTime)) + s.metrics.BatchesSubmitted().Inc() + s.metrics.SubmissionGasUsedWei().Set(float64(receipt.GasUsed)) + s.metrics.SubmissionTimestamp().Set(float64(time.Now().UnixNano() / 1e6)) case err := <-s.ctx.Done(): log.Error(name+" service shutting down", "err", err)