1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use {
    crate::{bank::Bank, cost_model::CostModel},
    crossbeam_channel::{Receiver, Sender},
    log::*,
    solana_sdk::{clock::Slot, signature::Signature, transaction::SanitizedTransaction},
    std::{
        sync::{Arc, RwLock},
        thread::{self, Builder, JoinHandle},
    },
};

pub enum TransactionCostMetrics {
    TransactionCostDetail {
        slot: Slot,
        tx_signature: Signature,
        signature_cost: u64,
        write_lock_cost: u64,
        data_bytes_cost: u64,
        execution_cost: u64,
    },
}

pub struct TransactionCostMetricsSender {
    cost_model: Arc<RwLock<CostModel>>,
    metrics_sender: Sender<TransactionCostMetrics>,
}

impl TransactionCostMetricsSender {
    pub fn new(
        cost_model: Arc<RwLock<CostModel>>,
        metrics_sender: Sender<TransactionCostMetrics>,
    ) -> Self {
        Self {
            cost_model,
            metrics_sender,
        }
    }

    pub fn send_cost_details<'a>(
        &self,
        bank: Arc<Bank>,
        txs: impl Iterator<Item = &'a SanitizedTransaction>,
    ) {
        let cost_model = self.cost_model.read().unwrap();
        txs.for_each(|tx| {
            let cost = cost_model.calculate_cost(tx);
            self.metrics_sender
                .send(TransactionCostMetrics::TransactionCostDetail {
                    slot: bank.slot(),
                    tx_signature: *tx.signature(),
                    signature_cost: cost.signature_cost,
                    write_lock_cost: cost.write_lock_cost,
                    data_bytes_cost: cost.data_bytes_cost,
                    execution_cost: cost.execution_cost,
                })
                .unwrap_or_else(|err| {
                    warn!(
                        "transaction cost metrics service report cost detail failed: {:?}",
                        err
                    )
                });
        });
    }
}

pub struct TransactionCostMetricsService {
    thread_hdl: JoinHandle<()>,
}

impl TransactionCostMetricsService {
    pub fn new(transaction_cost_metrics_receiver: Receiver<TransactionCostMetrics>) -> Self {
        let thread_hdl = Builder::new()
            .name("transaction_cost_metrics_service".to_string())
            .spawn(move || {
                Self::service_loop(transaction_cost_metrics_receiver);
            })
            .unwrap();

        Self { thread_hdl }
    }

    pub fn join(self) -> thread::Result<()> {
        self.thread_hdl.join()
    }

    fn service_loop(transaction_cost_metrics_receiver: Receiver<TransactionCostMetrics>) {
        for tx_cost_metrics in transaction_cost_metrics_receiver.iter() {
            match tx_cost_metrics {
                TransactionCostMetrics::TransactionCostDetail {
                    slot,
                    tx_signature,
                    signature_cost,
                    write_lock_cost,
                    data_bytes_cost,
                    execution_cost,
                } => {
                    // report transaction cost details per slot|signature
                    datapoint_trace!(
                        "transaction-cost-details",
                        ("slot", slot as i64, i64),
                        ("tx_signature", tx_signature.to_string(), String),
                        ("signature_cost", signature_cost as i64, i64),
                        ("write_lock_cost", write_lock_cost as i64, i64),
                        ("data_bytes_cost", data_bytes_cost as i64, i64),
                        ("execution_cost", execution_cost as i64, i64),
                    );
                }
            }
        }
    }
}