use {
crate::{
poh_recorder::{PohRecorderError, Record},
record_channels::{RecordSender, RecordSenderError},
},
solana_clock::BankId,
solana_entry::entry::hash_transactions,
solana_hash::Hash,
solana_measure::measure_us,
solana_transaction::versioned::VersionedTransaction,
std::num::Saturating,
};
#[derive(Default, Debug)]
pub struct RecordTransactionsTimings {
pub processing_results_to_transactions_us: Saturating<u64>,
pub hash_us: Saturating<u64>,
pub poh_record_us: Saturating<u64>,
}
impl RecordTransactionsTimings {
pub fn accumulate(&mut self, other: &RecordTransactionsTimings) {
self.processing_results_to_transactions_us += other.processing_results_to_transactions_us;
self.hash_us += other.hash_us;
self.poh_record_us += other.poh_record_us;
}
}
pub struct RecordTransactionsSummary {
pub record_transactions_timings: RecordTransactionsTimings,
pub result: Result<(), PohRecorderError>,
pub starting_transaction_index: Option<usize>,
}
#[derive(Clone, Debug)]
pub struct TransactionRecorder {
pub record_sender: RecordSender,
}
impl TransactionRecorder {
pub fn new(record_sender: RecordSender) -> Self {
Self { record_sender }
}
pub fn record_transactions(
&self,
bank_id: BankId,
transactions: Vec<VersionedTransaction>,
) -> RecordTransactionsSummary {
let mut record_transactions_timings = RecordTransactionsTimings::default();
let mut starting_transaction_index = None;
if !transactions.is_empty() {
let (hash, hash_us) = measure_us!(hash_transactions(&transactions));
record_transactions_timings.hash_us = Saturating(hash_us);
let (res, poh_record_us) =
measure_us!(self.record(bank_id, vec![hash], vec![transactions]));
record_transactions_timings.poh_record_us = Saturating(poh_record_us);
match res {
Ok(starting_index) => {
starting_transaction_index = starting_index;
}
Err(RecordSenderError::InactiveBankId | RecordSenderError::Shutdown) => {
return RecordTransactionsSummary {
record_transactions_timings,
result: Err(PohRecorderError::MaxHeightReached),
starting_transaction_index: None,
};
}
Err(RecordSenderError::Full) => {
return RecordTransactionsSummary {
record_transactions_timings,
result: Err(PohRecorderError::ChannelFull),
starting_transaction_index: None,
};
}
Err(RecordSenderError::Disconnected) => {
return RecordTransactionsSummary {
record_transactions_timings,
result: Err(PohRecorderError::ChannelDisconnected),
starting_transaction_index: None,
};
}
}
}
RecordTransactionsSummary {
record_transactions_timings,
result: Ok(()),
starting_transaction_index,
}
}
pub fn record(
&self,
bank_id: BankId,
mixins: Vec<Hash>,
transaction_batches: Vec<Vec<VersionedTransaction>>,
) -> Result<Option<usize>, RecordSenderError> {
self.record_sender
.try_send(Record::new(mixins, transaction_batches, bank_id))
}
}