diff --git a/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java b/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java index ad419c1c..404f01cf 100644 --- a/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java +++ b/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java @@ -92,7 +92,8 @@ public void start() { besuConfiguration, metricsSystem, profitabilityConfiguration()); final var transactionPoolProfitabilityMetrics = - new TransactionPoolProfitabilityMetrics(besuConfiguration, metricsSystem, profitabilityConfiguration()); + new TransactionPoolProfitabilityMetrics( + besuConfiguration, metricsSystem, profitabilityConfiguration()); try (Stream lines = Files.lines( @@ -145,15 +146,16 @@ public void start() { }); besuEventsService.addTransactionAddedListener( - transaction -> { - try { - transactionPoolProfitabilityMetrics.handleTransactionAdded(transaction); - } catch (Exception e) { - log.warn("Error recording transaction profitability metrics for {}: {}", - transaction.getHash(), e.getMessage()); - } - } - ); + transaction -> { + try { + transactionPoolProfitabilityMetrics.handleTransactionAdded(transaction); + } catch (Exception e) { + log.warn( + "Error recording transaction profitability metrics for {}: {}", + transaction.getHash(), + e.getMessage()); + } + }); } catch (Exception e) { throw new RuntimeException(e); diff --git a/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/metrics/TransactionPoolProfitabilityMetrics.java b/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/metrics/TransactionPoolProfitabilityMetrics.java index b1f58f64..4b89c734 100644 --- a/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/metrics/TransactionPoolProfitabilityMetrics.java +++ b/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/metrics/TransactionPoolProfitabilityMetrics.java @@ -15,6 +15,7 @@ package net.consensys.linea.sequencer.txpoolvalidation.metrics; import java.util.concurrent.atomic.AtomicReference; + import lombok.extern.slf4j.Slf4j; import net.consensys.linea.bl.TransactionProfitabilityCalculator; import net.consensys.linea.config.LineaProfitabilityConfiguration; @@ -24,20 +25,17 @@ import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.metrics.Counter; -import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; +import org.hyperledger.besu.plugin.services.metrics.Histogram; import org.hyperledger.besu.plugin.services.metrics.LabelledGauge; -import org.hyperledger.besu.plugin.services.metrics.Summary; +import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; /** - * Tracks profitability metrics for transactions in the transaction pool. - * Specifically monitors the ratio of profitable priority fee to actual priority fee: + * Tracks profitability metrics for transactions in the transaction pool. Specifically monitors the + * ratio of profitable priority fee to actual priority fee: * profitablePriorityFeePerGas/transaction.priorityFeePerGas - *

- * Provides: - * - Lowest ratio seen (minimum profitability) - * - Highest ratio seen (maximum profitability) - * - Running average ratio (average profitability) - * - Distribution histogram of ratios + * + *

Provides: - Lowest ratio seen (minimum profitability) - Highest ratio seen (maximum + * profitability) - Distribution histogram of ratios */ @Slf4j public class TransactionPoolProfitabilityMetrics { @@ -47,9 +45,7 @@ public class TransactionPoolProfitabilityMetrics { private final LineaProfitabilityConfiguration profitabilityConf; private final BesuConfiguration besuConfiguration; - private final LabelledMetric profitabilityHistogram; - - private final LabelledMetric

profitabilityRatioSummary; + private final LabelledMetric profitabilityHistogram; private final Counter invalidTransactionCount; // Thread-safe references for gauge values @@ -57,58 +53,43 @@ public class TransactionPoolProfitabilityMetrics { private final AtomicReference currentHighest = new AtomicReference<>(0.0); public TransactionPoolProfitabilityMetrics( - final BesuConfiguration besuConfiguration, - final MetricsSystem metricsSystem, - final LineaProfitabilityConfiguration profitabilityConf) { + final BesuConfiguration besuConfiguration, + final MetricsSystem metricsSystem, + final LineaProfitabilityConfiguration profitabilityConf) { this.besuConfiguration = besuConfiguration; this.profitabilityConf = profitabilityConf; this.profitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf); - // Distribution histogram - this.profitabilityHistogram = metricsSystem.createLabelledCounter( - LineaMetricCategory.PROFITABILITY, - "txpool_profitability_ratio", - "Distribution of transaction profitability ratios in TxPool", - "bucket" - ); - - // Min/Max/Avg gauges with DoubleSupplier - LabelledGauge lowestProfitabilityRatio = metricsSystem.createLabelledGauge( - LineaMetricCategory.PROFITABILITY, - "txpool_profitability_ratio_min", - "Lowest profitability ratio seen" - ); + // Min/Max gauges with DoubleSupplier + LabelledGauge lowestProfitabilityRatio = + metricsSystem.createLabelledGauge( + LineaMetricCategory.PROFITABILITY, + "txpool_profitability_ratio_min", + "Lowest profitability ratio seen"); lowestProfitabilityRatio.labels(currentLowest::get); - LabelledGauge highestProfitabilityRatio = metricsSystem.createLabelledGauge( - LineaMetricCategory.PROFITABILITY, - "txpool_profitability_ratio_max", - "Highest profitability ratio seen" - ); + LabelledGauge highestProfitabilityRatio = + metricsSystem.createLabelledGauge( + LineaMetricCategory.PROFITABILITY, + "txpool_profitability_ratio_max", + "Highest profitability ratio seen"); highestProfitabilityRatio.labels(currentHighest::get); - LabelledGauge averageProfitabilityRatio = metricsSystem.createLabelledGauge( - LineaMetricCategory.PROFITABILITY, - "txpool_profitability_ratio_avg", - "Average profitability ratio" - ); - AtomicReference currentAverage = new AtomicReference<>(0.0); - averageProfitabilityRatio.labels(currentAverage::get); - // Running statistics - this.profitabilityRatioSummary = metricsSystem.createLabelledSummary( - LineaMetricCategory.PROFITABILITY, - "txpool_profitability_ratio_summary", - "Summary statistics of profitability ratios", - "type" - ); - - this.invalidTransactionCount = metricsSystem.createCounter( - LineaMetricCategory.PROFITABILITY, - "txpool_invalid_transaction_count", - "Number of transactions that couldn't be processed for profitability" - ); + this.profitabilityHistogram = + metricsSystem.createLabelledHistogram( + LineaMetricCategory.PROFITABILITY, + "txpool_profitability_ratio_summary", + "Summary statistics of profitability ratios", + HISTOGRAM_BUCKETS, + "type"); + + this.invalidTransactionCount = + metricsSystem.createCounter( + LineaMetricCategory.PROFITABILITY, + "txpool_invalid_transaction_count", + "Number of transactions that couldn't be processed for profitability"); // Pre-create histogram buckets for (double bucket : HISTOGRAM_BUCKETS) { @@ -124,27 +105,23 @@ public void handleTransactionAdded(Transaction transaction) { return; } - Wei profitablePriorityFeePerGas = profitabilityCalculator.profitablePriorityFeePerGas( - transaction, - profitabilityConf.txPoolMinMargin(), - transaction.getGasLimit(), - besuConfiguration.getMinGasPrice() - ); + Wei profitablePriorityFeePerGas = + profitabilityCalculator.profitablePriorityFeePerGas( + transaction, + profitabilityConf.txPoolMinMargin(), + transaction.getGasLimit(), + besuConfiguration.getMinGasPrice()); Wei actualPriorityFeePerGas = Wei.fromQuantity(transaction.getMaxPriorityFeePerGas().get()); if (actualPriorityFeePerGas.toLong() > 0) { - double ratio = profitablePriorityFeePerGas.toBigInteger().doubleValue() / - actualPriorityFeePerGas.toBigInteger().doubleValue(); + double ratio = + profitablePriorityFeePerGas.toBigInteger().doubleValue() + / actualPriorityFeePerGas.toBigInteger().doubleValue(); updateRunningStats(ratio); - updateHistogramBuckets(ratio); - log.trace( - "Recorded profitability ratio {} for tx {}", - ratio, - transaction.getHash() - ); + log.trace("Recorded profitability ratio {} for tx {}", ratio, transaction.getHash()); } else { invalidTransactionCount.inc(); log.trace("Skipping transaction {} - zero priority fee", transaction.getHash()); @@ -152,10 +129,9 @@ public void handleTransactionAdded(Transaction transaction) { } catch (Exception e) { invalidTransactionCount.inc(); log.warn( - "Failed to record profitability metrics for tx {}: {}", - transaction.getHash(), - e.getMessage() - ); + "Failed to record profitability metrics for tx {}: {}", + transaction.getHash(), + e.getMessage()); } } @@ -167,14 +143,6 @@ private void updateRunningStats(double ratio) { currentHighest.updateAndGet(current -> Math.max(current, ratio)); // Record the observation in summary - profitabilityRatioSummary.labels("profitability").observe(ratio); - } - - private void updateHistogramBuckets(double ratio) { - for (double bucket : HISTOGRAM_BUCKETS) { - if (ratio <= bucket) { - profitabilityHistogram.labels(String.format("le_%.1f", bucket)).inc(); - } - } + profitabilityHistogram.labels("profitability").observe(ratio); } -} \ No newline at end of file +} diff --git a/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/metrics/SelectorProfitabilityMetrics.java b/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/metrics/SelectorProfitabilityMetrics.java index d39798e5..9dcfd554 100644 --- a/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/metrics/SelectorProfitabilityMetrics.java +++ b/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/metrics/SelectorProfitabilityMetrics.java @@ -20,19 +20,20 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.metrics.Histogram; import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; -import org.hyperledger.besu.plugin.services.metrics.Summary; @Slf4j public class SelectorProfitabilityMetrics { - private final LabelledMetric summaries; + private final LabelledMetric summaries; public SelectorProfitabilityMetrics(final MetricsSystem metricsSystem) { this.summaries = - metricsSystem.createLabelledSummary( + metricsSystem.createLabelledHistogram( BesuMetricCategory.ETHEREUM, - "selection_priority_fee_ratio", + "selection_profitability_ratio", "The ratio between the effective priority fee and the calculated one", + new double[] {0.9, 1.0, 1.2, 2, 5, 10, 100, 1000}, "phase"); } diff --git a/sequencer/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java b/sequencer/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java index f56557b8..1c9f1d90 100644 --- a/sequencer/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java +++ b/sequencer/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java @@ -22,14 +22,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; import net.consensys.linea.config.LineaProfitabilityCliOptions; import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaTransactionSelectorCliOptions; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; +import net.consensys.linea.sequencer.txselection.metrics.SelectorProfitabilityMetrics; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.bouncycastle.crypto.digests.KeccakDigest; @@ -37,10 +36,12 @@ import org.hyperledger.besu.datatypes.PendingTransaction; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; import org.hyperledger.besu.plugin.data.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -64,11 +65,12 @@ public class ProfitableTransactionSelectorTest { .variableCostWei(VARIABLE_GAS_COST_WEI) .build(); private TestableProfitableTransactionSelector transactionSelector; - private Map profitablePriorityFeeCache; + private MetricsSystem metricsSystem = new NoOpMetricsSystem(); + private SelectorProfitabilityMetrics selectorProfitabilityMetrics; @BeforeEach public void initialize() { - profitablePriorityFeeCache = new HashMap<>(); + selectorProfitabilityMetrics = new SelectorProfitabilityMetrics(metricsSystem); transactionSelector = newSelectorForNewBlock(); transactionSelector.reset(); } @@ -77,7 +79,7 @@ private TestableProfitableTransactionSelector newSelectorForNewBlock() { final var blockchainService = mock(BlockchainService.class); when(blockchainService.getNextBlockBaseFee()).thenReturn(Optional.of(BASE_FEE)); return new TestableProfitableTransactionSelector( - blockchainService, txSelectorConf, profitabilityConf, profitablePriorityFeeCache); + blockchainService, txSelectorConf, profitabilityConf, selectorProfitabilityMetrics); } @Test @@ -335,22 +337,6 @@ public void profitableAndUnprofitableTxsMix() { SELECTED); } - @Test - public void shouldAddToProfitablePriorityFeeWhenSelect() { - final var mockTransactionProcessingResult = mockTransactionProcessingResult(21000); - final var mockEvaluationContext = - mockEvaluationContext(false, 100, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 21000); - verifyTransactionSelection( - transactionSelector, - mockEvaluationContext, - mockTransactionProcessingResult, - SELECTED, - SELECTED); - - assertThat(profitablePriorityFeeCache) - .containsOnlyKeys(mockEvaluationContext.getPendingTransaction().getTransaction().getHash()); - } - private void verifyTransactionSelection( final ProfitableTransactionSelector selector, final TestTransactionEvaluationContext evaluationContext, @@ -431,8 +417,8 @@ private static class TestableProfitableTransactionSelector extends ProfitableTra final BlockchainService blockchainService, final LineaTransactionSelectorConfiguration txSelectorConf, final LineaProfitabilityConfiguration profitabilityConf, - final Map profitablePriorityFeeCache) { - super(blockchainService, txSelectorConf, profitabilityConf, profitablePriorityFeeCache); + final SelectorProfitabilityMetrics selectorProfitabilityMetrics) { + super(blockchainService, txSelectorConf, profitabilityConf, selectorProfitabilityMetrics); } boolean isUnprofitableTxCached(final Hash txHash) {