eliminate lock on record (#15929)
* eliminate lock on record * use same error as MaxHeightReached * clippy * review feedback * refactor should_tick code * pr feedback
This commit is contained in:
committed by
GitHub
parent
6271665ba6
commit
57ba86c821
@ -66,6 +66,8 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
|||||||
let (exit, poh_recorder, poh_service, _signal_receiver) =
|
let (exit, poh_recorder, poh_service, _signal_receiver) =
|
||||||
create_test_recorder(&bank, &blockstore, None);
|
create_test_recorder(&bank, &blockstore, None);
|
||||||
|
|
||||||
|
let recorder = poh_recorder.lock().unwrap().recorder();
|
||||||
|
|
||||||
let tx = test_tx();
|
let tx = test_tx();
|
||||||
let len = 4096;
|
let len = 4096;
|
||||||
let chunk_size = 1024;
|
let chunk_size = 1024;
|
||||||
@ -88,6 +90,7 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
|||||||
&s,
|
&s,
|
||||||
None::<Box<dyn Fn()>>,
|
None::<Box<dyn Fn()>>,
|
||||||
None,
|
None,
|
||||||
|
&recorder,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
cluster_info::ClusterInfo,
|
cluster_info::ClusterInfo,
|
||||||
packet_hasher::PacketHasher,
|
packet_hasher::PacketHasher,
|
||||||
poh_recorder::{PohRecorder, PohRecorderError, WorkingBankEntry},
|
poh_recorder::{PohRecorder, PohRecorderError, TransactionRecorder, WorkingBankEntry},
|
||||||
poh_service::{self, PohService},
|
poh_service::{self, PohService},
|
||||||
};
|
};
|
||||||
use crossbeam_channel::{Receiver as CrossbeamReceiver, RecvTimeoutError};
|
use crossbeam_channel::{Receiver as CrossbeamReceiver, RecvTimeoutError};
|
||||||
@ -295,6 +295,7 @@ impl BankingStage {
|
|||||||
gossip_vote_sender: &ReplayVoteSender,
|
gossip_vote_sender: &ReplayVoteSender,
|
||||||
test_fn: Option<impl Fn()>,
|
test_fn: Option<impl Fn()>,
|
||||||
banking_stage_stats: Option<&BankingStageStats>,
|
banking_stage_stats: Option<&BankingStageStats>,
|
||||||
|
recorder: &TransactionRecorder,
|
||||||
) {
|
) {
|
||||||
let mut rebuffered_packets_len = 0;
|
let mut rebuffered_packets_len = 0;
|
||||||
let mut new_tx_count = 0;
|
let mut new_tx_count = 0;
|
||||||
@ -323,7 +324,7 @@ impl BankingStage {
|
|||||||
Self::process_packets_transactions(
|
Self::process_packets_transactions(
|
||||||
&bank,
|
&bank,
|
||||||
&bank_creation_time,
|
&bank_creation_time,
|
||||||
&poh_recorder,
|
&recorder,
|
||||||
&msgs,
|
&msgs,
|
||||||
original_unprocessed_indexes.to_owned(),
|
original_unprocessed_indexes.to_owned(),
|
||||||
transaction_status_sender.clone(),
|
transaction_status_sender.clone(),
|
||||||
@ -428,6 +429,7 @@ impl BankingStage {
|
|||||||
transaction_status_sender: Option<TransactionStatusSender>,
|
transaction_status_sender: Option<TransactionStatusSender>,
|
||||||
gossip_vote_sender: &ReplayVoteSender,
|
gossip_vote_sender: &ReplayVoteSender,
|
||||||
banking_stage_stats: &BankingStageStats,
|
banking_stage_stats: &BankingStageStats,
|
||||||
|
recorder: &TransactionRecorder,
|
||||||
) -> BufferedPacketsDecision {
|
) -> BufferedPacketsDecision {
|
||||||
let bank_start;
|
let bank_start;
|
||||||
let (
|
let (
|
||||||
@ -467,6 +469,7 @@ impl BankingStage {
|
|||||||
gossip_vote_sender,
|
gossip_vote_sender,
|
||||||
None::<Box<dyn Fn()>>,
|
None::<Box<dyn Fn()>>,
|
||||||
Some(banking_stage_stats),
|
Some(banking_stage_stats),
|
||||||
|
recorder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
BufferedPacketsDecision::Forward => {
|
BufferedPacketsDecision::Forward => {
|
||||||
@ -544,6 +547,7 @@ impl BankingStage {
|
|||||||
gossip_vote_sender: ReplayVoteSender,
|
gossip_vote_sender: ReplayVoteSender,
|
||||||
duplicates: &Arc<Mutex<(LruCache<u64, ()>, PacketHasher)>>,
|
duplicates: &Arc<Mutex<(LruCache<u64, ()>, PacketHasher)>>,
|
||||||
) {
|
) {
|
||||||
|
let recorder = poh_recorder.lock().unwrap().recorder();
|
||||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||||
let mut buffered_packets = VecDeque::with_capacity(batch_limit);
|
let mut buffered_packets = VecDeque::with_capacity(batch_limit);
|
||||||
let banking_stage_stats = BankingStageStats::new(id);
|
let banking_stage_stats = BankingStageStats::new(id);
|
||||||
@ -552,13 +556,14 @@ impl BankingStage {
|
|||||||
let decision = Self::process_buffered_packets(
|
let decision = Self::process_buffered_packets(
|
||||||
&my_pubkey,
|
&my_pubkey,
|
||||||
&socket,
|
&socket,
|
||||||
poh_recorder,
|
&poh_recorder,
|
||||||
cluster_info,
|
cluster_info,
|
||||||
&mut buffered_packets,
|
&mut buffered_packets,
|
||||||
enable_forwarding,
|
enable_forwarding,
|
||||||
transaction_status_sender.clone(),
|
transaction_status_sender.clone(),
|
||||||
&gossip_vote_sender,
|
&gossip_vote_sender,
|
||||||
&banking_stage_stats,
|
&banking_stage_stats,
|
||||||
|
&recorder,
|
||||||
);
|
);
|
||||||
if matches!(decision, BufferedPacketsDecision::Hold)
|
if matches!(decision, BufferedPacketsDecision::Hold)
|
||||||
|| matches!(decision, BufferedPacketsDecision::ForwardAndHold)
|
|| matches!(decision, BufferedPacketsDecision::ForwardAndHold)
|
||||||
@ -592,6 +597,7 @@ impl BankingStage {
|
|||||||
&mut buffered_packets,
|
&mut buffered_packets,
|
||||||
&banking_stage_stats,
|
&banking_stage_stats,
|
||||||
duplicates,
|
duplicates,
|
||||||
|
&recorder,
|
||||||
) {
|
) {
|
||||||
Ok(()) | Err(RecvTimeoutError::Timeout) => (),
|
Ok(()) | Err(RecvTimeoutError::Timeout) => (),
|
||||||
Err(RecvTimeoutError::Disconnected) => break,
|
Err(RecvTimeoutError::Disconnected) => break,
|
||||||
@ -625,7 +631,7 @@ impl BankingStage {
|
|||||||
bank_slot: Slot,
|
bank_slot: Slot,
|
||||||
txs: &[Transaction],
|
txs: &[Transaction],
|
||||||
results: &[TransactionExecutionResult],
|
results: &[TransactionExecutionResult],
|
||||||
poh: &Arc<Mutex<PohRecorder>>,
|
recorder: &TransactionRecorder,
|
||||||
) -> (Result<usize, PohRecorderError>, Vec<usize>) {
|
) -> (Result<usize, PohRecorderError>, Vec<usize>) {
|
||||||
let mut processed_generation = Measure::start("record::process_generation");
|
let mut processed_generation = Measure::start("record::process_generation");
|
||||||
let (processed_transactions, processed_transactions_indexes): (Vec<_>, Vec<_>) = results
|
let (processed_transactions, processed_transactions_indexes): (Vec<_>, Vec<_>) = results
|
||||||
@ -655,10 +661,7 @@ impl BankingStage {
|
|||||||
|
|
||||||
let mut poh_record = Measure::start("record::poh_record");
|
let mut poh_record = Measure::start("record::poh_record");
|
||||||
// record and unlock will unlock all the successful transactions
|
// record and unlock will unlock all the successful transactions
|
||||||
let res = poh
|
let res = recorder.record(bank_slot, hash, processed_transactions);
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.record(bank_slot, hash, processed_transactions);
|
|
||||||
match res {
|
match res {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(PohRecorderError::MaxHeightReached) => {
|
Err(PohRecorderError::MaxHeightReached) => {
|
||||||
@ -683,7 +686,7 @@ impl BankingStage {
|
|||||||
|
|
||||||
fn process_and_record_transactions_locked(
|
fn process_and_record_transactions_locked(
|
||||||
bank: &Arc<Bank>,
|
bank: &Arc<Bank>,
|
||||||
poh: &Arc<Mutex<PohRecorder>>,
|
poh: &TransactionRecorder,
|
||||||
batch: &TransactionBatch,
|
batch: &TransactionBatch,
|
||||||
transaction_status_sender: Option<TransactionStatusSender>,
|
transaction_status_sender: Option<TransactionStatusSender>,
|
||||||
gossip_vote_sender: &ReplayVoteSender,
|
gossip_vote_sender: &ReplayVoteSender,
|
||||||
@ -802,7 +805,7 @@ impl BankingStage {
|
|||||||
pub fn process_and_record_transactions(
|
pub fn process_and_record_transactions(
|
||||||
bank: &Arc<Bank>,
|
bank: &Arc<Bank>,
|
||||||
txs: &[Transaction],
|
txs: &[Transaction],
|
||||||
poh: &Arc<Mutex<PohRecorder>>,
|
poh: &TransactionRecorder,
|
||||||
chunk_offset: usize,
|
chunk_offset: usize,
|
||||||
transaction_status_sender: Option<TransactionStatusSender>,
|
transaction_status_sender: Option<TransactionStatusSender>,
|
||||||
gossip_vote_sender: &ReplayVoteSender,
|
gossip_vote_sender: &ReplayVoteSender,
|
||||||
@ -846,7 +849,7 @@ impl BankingStage {
|
|||||||
bank: &Arc<Bank>,
|
bank: &Arc<Bank>,
|
||||||
bank_creation_time: &Instant,
|
bank_creation_time: &Instant,
|
||||||
transactions: &[Transaction],
|
transactions: &[Transaction],
|
||||||
poh: &Arc<Mutex<PohRecorder>>,
|
poh: &TransactionRecorder,
|
||||||
transaction_status_sender: Option<TransactionStatusSender>,
|
transaction_status_sender: Option<TransactionStatusSender>,
|
||||||
gossip_vote_sender: &ReplayVoteSender,
|
gossip_vote_sender: &ReplayVoteSender,
|
||||||
) -> (usize, Vec<usize>) {
|
) -> (usize, Vec<usize>) {
|
||||||
@ -1017,7 +1020,7 @@ impl BankingStage {
|
|||||||
fn process_packets_transactions(
|
fn process_packets_transactions(
|
||||||
bank: &Arc<Bank>,
|
bank: &Arc<Bank>,
|
||||||
bank_creation_time: &Instant,
|
bank_creation_time: &Instant,
|
||||||
poh: &Arc<Mutex<PohRecorder>>,
|
poh: &TransactionRecorder,
|
||||||
msgs: &Packets,
|
msgs: &Packets,
|
||||||
packet_indexes: Vec<usize>,
|
packet_indexes: Vec<usize>,
|
||||||
transaction_status_sender: Option<TransactionStatusSender>,
|
transaction_status_sender: Option<TransactionStatusSender>,
|
||||||
@ -1131,6 +1134,7 @@ impl BankingStage {
|
|||||||
buffered_packets: &mut UnprocessedPackets,
|
buffered_packets: &mut UnprocessedPackets,
|
||||||
banking_stage_stats: &BankingStageStats,
|
banking_stage_stats: &BankingStageStats,
|
||||||
duplicates: &Arc<Mutex<(LruCache<u64, ()>, PacketHasher)>>,
|
duplicates: &Arc<Mutex<(LruCache<u64, ()>, PacketHasher)>>,
|
||||||
|
recorder: &TransactionRecorder,
|
||||||
) -> Result<(), RecvTimeoutError> {
|
) -> Result<(), RecvTimeoutError> {
|
||||||
let mut recv_time = Measure::start("process_packets_recv");
|
let mut recv_time = Measure::start("process_packets_recv");
|
||||||
let mms = verified_receiver.recv_timeout(recv_timeout)?;
|
let mms = verified_receiver.recv_timeout(recv_timeout)?;
|
||||||
@ -1173,7 +1177,7 @@ impl BankingStage {
|
|||||||
Self::process_packets_transactions(
|
Self::process_packets_transactions(
|
||||||
&bank,
|
&bank,
|
||||||
&bank_creation_time,
|
&bank_creation_time,
|
||||||
&poh,
|
recorder,
|
||||||
&msgs,
|
&msgs,
|
||||||
packet_indexes,
|
packet_indexes,
|
||||||
transaction_status_sender.clone(),
|
transaction_status_sender.clone(),
|
||||||
@ -1309,7 +1313,7 @@ pub fn create_test_recorder(
|
|||||||
) {
|
) {
|
||||||
let exit = Arc::new(AtomicBool::new(false));
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
let poh_config = Arc::new(poh_config.unwrap_or_default());
|
let poh_config = Arc::new(poh_config.unwrap_or_default());
|
||||||
let (mut poh_recorder, entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, entry_receiver, record_receiver) = PohRecorder::new(
|
||||||
bank.tick_height(),
|
bank.tick_height(),
|
||||||
bank.last_blockhash(),
|
bank.last_blockhash(),
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
@ -1330,6 +1334,7 @@ pub fn create_test_recorder(
|
|||||||
bank.ticks_per_slot(),
|
bank.ticks_per_slot(),
|
||||||
poh_service::DEFAULT_PINNED_CPU_CORE,
|
poh_service::DEFAULT_PINNED_CPU_CORE,
|
||||||
poh_service::DEFAULT_HASHES_PER_BATCH,
|
poh_service::DEFAULT_HASHES_PER_BATCH,
|
||||||
|
record_receiver,
|
||||||
);
|
);
|
||||||
|
|
||||||
(exit, poh_recorder, poh_service, entry_receiver)
|
(exit, poh_recorder, poh_service, entry_receiver)
|
||||||
@ -1339,7 +1344,7 @@ pub fn create_test_recorder(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
cluster_info::Node, poh_recorder::WorkingBank,
|
cluster_info::Node, poh_recorder::Record, poh_recorder::WorkingBank,
|
||||||
transaction_status_service::TransactionStatusService,
|
transaction_status_service::TransactionStatusService,
|
||||||
};
|
};
|
||||||
use crossbeam_channel::unbounded;
|
use crossbeam_channel::unbounded;
|
||||||
@ -1359,7 +1364,15 @@ mod tests {
|
|||||||
transaction::TransactionError,
|
transaction::TransactionError,
|
||||||
};
|
};
|
||||||
use solana_transaction_status::TransactionWithStatusMeta;
|
use solana_transaction_status::TransactionWithStatusMeta;
|
||||||
use std::{net::SocketAddr, path::Path, sync::atomic::Ordering, thread::sleep};
|
use std::{
|
||||||
|
net::SocketAddr,
|
||||||
|
path::Path,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
mpsc::Receiver,
|
||||||
|
},
|
||||||
|
thread::sleep,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_banking_stage_shutdown1() {
|
fn test_banking_stage_shutdown1() {
|
||||||
@ -1684,6 +1697,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bank_record_transactions() {
|
fn test_bank_record_transactions() {
|
||||||
|
solana_logger::setup();
|
||||||
|
|
||||||
let GenesisConfigInfo {
|
let GenesisConfigInfo {
|
||||||
genesis_config,
|
genesis_config,
|
||||||
mint_keypair,
|
mint_keypair,
|
||||||
@ -1701,7 +1716,8 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
let (poh_recorder, entry_receiver) = PohRecorder::new(
|
let (poh_recorder, entry_receiver, record_receiver) = PohRecorder::new(
|
||||||
|
// TODO use record_receiver
|
||||||
bank.tick_height(),
|
bank.tick_height(),
|
||||||
bank.last_blockhash(),
|
bank.last_blockhash(),
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
@ -1712,8 +1728,11 @@ mod tests {
|
|||||||
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
let recorder = poh_recorder.recorder();
|
||||||
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
||||||
|
|
||||||
|
let (poh_simulator, exit) = simulate_poh(record_receiver, &poh_recorder);
|
||||||
|
|
||||||
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
||||||
let pubkey = solana_sdk::pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let keypair2 = Keypair::new();
|
let keypair2 = Keypair::new();
|
||||||
@ -1725,12 +1744,8 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let mut results = vec![(Ok(()), None), (Ok(()), None)];
|
let mut results = vec![(Ok(()), None), (Ok(()), None)];
|
||||||
let _ = BankingStage::record_transactions(
|
let _ =
|
||||||
bank.slot(),
|
BankingStage::record_transactions(bank.slot(), &transactions, &results, &recorder);
|
||||||
&transactions,
|
|
||||||
&results,
|
|
||||||
&poh_recorder,
|
|
||||||
);
|
|
||||||
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
|
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
|
||||||
assert_eq!(entry.transactions.len(), transactions.len());
|
assert_eq!(entry.transactions.len(), transactions.len());
|
||||||
|
|
||||||
@ -1742,12 +1757,8 @@ mod tests {
|
|||||||
)),
|
)),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let (res, retryable) = BankingStage::record_transactions(
|
let (res, retryable) =
|
||||||
bank.slot(),
|
BankingStage::record_transactions(bank.slot(), &transactions, &results, &recorder);
|
||||||
&transactions,
|
|
||||||
&results,
|
|
||||||
&poh_recorder,
|
|
||||||
);
|
|
||||||
res.unwrap();
|
res.unwrap();
|
||||||
assert!(retryable.is_empty());
|
assert!(retryable.is_empty());
|
||||||
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
|
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
|
||||||
@ -1755,12 +1766,8 @@ mod tests {
|
|||||||
|
|
||||||
// Other TransactionErrors should not be recorded
|
// Other TransactionErrors should not be recorded
|
||||||
results[0] = (Err(TransactionError::AccountNotFound), None);
|
results[0] = (Err(TransactionError::AccountNotFound), None);
|
||||||
let (res, retryable) = BankingStage::record_transactions(
|
let (res, retryable) =
|
||||||
bank.slot(),
|
BankingStage::record_transactions(bank.slot(), &transactions, &results, &recorder);
|
||||||
&transactions,
|
|
||||||
&results,
|
|
||||||
&poh_recorder,
|
|
||||||
);
|
|
||||||
res.unwrap();
|
res.unwrap();
|
||||||
assert!(retryable.is_empty());
|
assert!(retryable.is_empty());
|
||||||
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
|
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
|
||||||
@ -1773,7 +1780,7 @@ mod tests {
|
|||||||
bank.slot() + 1,
|
bank.slot() + 1,
|
||||||
&transactions,
|
&transactions,
|
||||||
&results,
|
&results,
|
||||||
&poh_recorder,
|
&recorder,
|
||||||
);
|
);
|
||||||
assert_matches!(res, Err(PohRecorderError::MaxHeightReached));
|
assert_matches!(res, Err(PohRecorderError::MaxHeightReached));
|
||||||
// The first result was an error so it's filtered out. The second result was Ok(),
|
// The first result was an error so it's filtered out. The second result was Ok(),
|
||||||
@ -1781,6 +1788,9 @@ mod tests {
|
|||||||
assert_eq!(retryable, vec![1]);
|
assert_eq!(retryable, vec![1]);
|
||||||
// Should receive nothing from PohRecorder b/c record failed
|
// Should receive nothing from PohRecorder b/c record failed
|
||||||
assert!(entry_receiver.try_recv().is_err());
|
assert!(entry_receiver.try_recv().is_err());
|
||||||
|
|
||||||
|
exit.store(true, Ordering::Relaxed);
|
||||||
|
let _ = poh_simulator.join();
|
||||||
}
|
}
|
||||||
Blockstore::destroy(&ledger_path).unwrap();
|
Blockstore::destroy(&ledger_path).unwrap();
|
||||||
}
|
}
|
||||||
@ -2052,7 +2062,7 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
let (poh_recorder, entry_receiver) = PohRecorder::new(
|
let (poh_recorder, entry_receiver, record_receiver) = PohRecorder::new(
|
||||||
bank.tick_height(),
|
bank.tick_height(),
|
||||||
bank.last_blockhash(),
|
bank.last_blockhash(),
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
@ -2063,15 +2073,18 @@ mod tests {
|
|||||||
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
let recorder = poh_recorder.recorder();
|
||||||
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
||||||
|
|
||||||
|
let (poh_simulator, exit) = simulate_poh(record_receiver, &poh_recorder);
|
||||||
|
|
||||||
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
||||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||||
|
|
||||||
BankingStage::process_and_record_transactions(
|
BankingStage::process_and_record_transactions(
|
||||||
&bank,
|
&bank,
|
||||||
&transactions,
|
&transactions,
|
||||||
&poh_recorder,
|
&recorder,
|
||||||
0,
|
0,
|
||||||
None,
|
None,
|
||||||
&gossip_vote_sender,
|
&gossip_vote_sender,
|
||||||
@ -2108,7 +2121,7 @@ mod tests {
|
|||||||
BankingStage::process_and_record_transactions(
|
BankingStage::process_and_record_transactions(
|
||||||
&bank,
|
&bank,
|
||||||
&transactions,
|
&transactions,
|
||||||
&poh_recorder,
|
&recorder,
|
||||||
0,
|
0,
|
||||||
None,
|
None,
|
||||||
&gossip_vote_sender,
|
&gossip_vote_sender,
|
||||||
@ -2117,11 +2130,36 @@ mod tests {
|
|||||||
Err(PohRecorderError::MaxHeightReached)
|
Err(PohRecorderError::MaxHeightReached)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
exit.store(true, Ordering::Relaxed);
|
||||||
|
let _ = poh_simulator.join();
|
||||||
|
|
||||||
assert_eq!(bank.get_balance(&pubkey), 1);
|
assert_eq!(bank.get_balance(&pubkey), 1);
|
||||||
}
|
}
|
||||||
Blockstore::destroy(&ledger_path).unwrap();
|
Blockstore::destroy(&ledger_path).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn simulate_poh(
|
||||||
|
record_receiver: Receiver<Record>,
|
||||||
|
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
||||||
|
) -> (JoinHandle<()>, Arc<AtomicBool>) {
|
||||||
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
|
let exit_ = exit.clone();
|
||||||
|
let poh_recorder = poh_recorder.clone();
|
||||||
|
let tick_producer = Builder::new()
|
||||||
|
.name("solana-simulate_poh".to_string())
|
||||||
|
.spawn(move || loop {
|
||||||
|
PohService::read_record_receiver_and_process(
|
||||||
|
&poh_recorder,
|
||||||
|
&record_receiver,
|
||||||
|
Duration::from_millis(10),
|
||||||
|
);
|
||||||
|
if exit_.load(Ordering::Relaxed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(tick_producer.unwrap(), exit)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bank_process_and_record_transactions_account_in_use() {
|
fn test_bank_process_and_record_transactions_account_in_use() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
@ -2150,7 +2188,7 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
let (poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (poh_recorder, _entry_receiver, record_receiver) = PohRecorder::new(
|
||||||
bank.tick_height(),
|
bank.tick_height(),
|
||||||
bank.last_blockhash(),
|
bank.last_blockhash(),
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
@ -2161,21 +2199,27 @@ mod tests {
|
|||||||
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
let recorder = poh_recorder.recorder();
|
||||||
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
||||||
|
|
||||||
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
||||||
|
|
||||||
|
let (poh_simulator, exit) = simulate_poh(record_receiver, &poh_recorder);
|
||||||
|
|
||||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||||
|
|
||||||
let (result, unprocessed) = BankingStage::process_and_record_transactions(
|
let (result, unprocessed) = BankingStage::process_and_record_transactions(
|
||||||
&bank,
|
&bank,
|
||||||
&transactions,
|
&transactions,
|
||||||
&poh_recorder,
|
&recorder,
|
||||||
0,
|
0,
|
||||||
None,
|
None,
|
||||||
&gossip_vote_sender,
|
&gossip_vote_sender,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
exit.store(true, Ordering::Relaxed);
|
||||||
|
let _ = poh_simulator.join();
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert_eq!(unprocessed.len(), 1);
|
assert_eq!(unprocessed.len(), 1);
|
||||||
}
|
}
|
||||||
@ -2245,7 +2289,7 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
let (poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (poh_recorder, _entry_receiver, record_receiver) = PohRecorder::new(
|
||||||
bank.tick_height(),
|
bank.tick_height(),
|
||||||
bank.last_blockhash(),
|
bank.last_blockhash(),
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
@ -2257,9 +2301,12 @@ mod tests {
|
|||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Poh Recorder has not working bank, so should throw MaxHeightReached error on
|
// Poh Recorder has no working bank, so should throw MaxHeightReached error on
|
||||||
// record
|
// record
|
||||||
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
let recorder = poh_recorder.recorder();
|
||||||
|
|
||||||
|
let (poh_simulator, exit) =
|
||||||
|
simulate_poh(record_receiver, &Arc::new(Mutex::new(poh_recorder)));
|
||||||
|
|
||||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||||
|
|
||||||
@ -2268,7 +2315,7 @@ mod tests {
|
|||||||
&bank,
|
&bank,
|
||||||
&Instant::now(),
|
&Instant::now(),
|
||||||
&transactions,
|
&transactions,
|
||||||
&poh_recorder,
|
&recorder,
|
||||||
None,
|
None,
|
||||||
&gossip_vote_sender,
|
&gossip_vote_sender,
|
||||||
);
|
);
|
||||||
@ -2278,6 +2325,9 @@ mod tests {
|
|||||||
retryable_txs.sort_unstable();
|
retryable_txs.sort_unstable();
|
||||||
let expected: Vec<usize> = (0..transactions.len()).collect();
|
let expected: Vec<usize> = (0..transactions.len()).collect();
|
||||||
assert_eq!(retryable_txs, expected);
|
assert_eq!(retryable_txs, expected);
|
||||||
|
|
||||||
|
exit.store(true, Ordering::Relaxed);
|
||||||
|
let _ = poh_simulator.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
Blockstore::destroy(&ledger_path).unwrap();
|
Blockstore::destroy(&ledger_path).unwrap();
|
||||||
@ -2324,7 +2374,7 @@ mod tests {
|
|||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
let blockstore = Arc::new(blockstore);
|
let blockstore = Arc::new(blockstore);
|
||||||
let (poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (poh_recorder, _entry_receiver, record_receiver) = PohRecorder::new(
|
||||||
bank.tick_height(),
|
bank.tick_height(),
|
||||||
bank.last_blockhash(),
|
bank.last_blockhash(),
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
@ -2335,8 +2385,11 @@ mod tests {
|
|||||||
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
let recorder = poh_recorder.recorder();
|
||||||
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
||||||
|
|
||||||
|
let (poh_simulator, exit) = simulate_poh(record_receiver, &poh_recorder);
|
||||||
|
|
||||||
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
||||||
|
|
||||||
let shreds = entries_to_test_shreds(entries, bank.slot(), 0, true, 0);
|
let shreds = entries_to_test_shreds(entries, bank.slot(), 0, true, 0);
|
||||||
@ -2355,7 +2408,7 @@ mod tests {
|
|||||||
let _ = BankingStage::process_and_record_transactions(
|
let _ = BankingStage::process_and_record_transactions(
|
||||||
&bank,
|
&bank,
|
||||||
&transactions,
|
&transactions,
|
||||||
&poh_recorder,
|
&recorder,
|
||||||
0,
|
0,
|
||||||
Some(TransactionStatusSender {
|
Some(TransactionStatusSender {
|
||||||
sender: transaction_status_sender,
|
sender: transaction_status_sender,
|
||||||
@ -2388,10 +2441,14 @@ mod tests {
|
|||||||
assert_eq!(meta, None);
|
assert_eq!(meta, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exit.store(true, Ordering::Relaxed);
|
||||||
|
let _ = poh_simulator.join();
|
||||||
}
|
}
|
||||||
Blockstore::destroy(&ledger_path).unwrap();
|
Blockstore::destroy(&ledger_path).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn setup_conflicting_transactions(
|
fn setup_conflicting_transactions(
|
||||||
ledger_path: &Path,
|
ledger_path: &Path,
|
||||||
) -> (
|
) -> (
|
||||||
@ -2399,6 +2456,8 @@ mod tests {
|
|||||||
Arc<Bank>,
|
Arc<Bank>,
|
||||||
Arc<Mutex<PohRecorder>>,
|
Arc<Mutex<PohRecorder>>,
|
||||||
Receiver<WorkingBankEntry>,
|
Receiver<WorkingBankEntry>,
|
||||||
|
JoinHandle<()>,
|
||||||
|
Arc<AtomicBool>,
|
||||||
) {
|
) {
|
||||||
Blockstore::destroy(&ledger_path).unwrap();
|
Blockstore::destroy(&ledger_path).unwrap();
|
||||||
let genesis_config_info = create_genesis_config(10_000);
|
let genesis_config_info = create_genesis_config(10_000);
|
||||||
@ -2410,7 +2469,7 @@ mod tests {
|
|||||||
let blockstore =
|
let blockstore =
|
||||||
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger");
|
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger");
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let (poh_recorder, entry_receiver) = PohRecorder::new(
|
let (poh_recorder, entry_receiver, record_receiver) = PohRecorder::new(
|
||||||
bank.tick_height(),
|
bank.tick_height(),
|
||||||
bank.last_blockhash(),
|
bank.last_blockhash(),
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
@ -2432,15 +2491,25 @@ mod tests {
|
|||||||
system_transaction::transfer(&mint_keypair, &pubkey1, 1, genesis_config.hash()),
|
system_transaction::transfer(&mint_keypair, &pubkey1, 1, genesis_config.hash()),
|
||||||
system_transaction::transfer(&mint_keypair, &pubkey2, 1, genesis_config.hash()),
|
system_transaction::transfer(&mint_keypair, &pubkey2, 1, genesis_config.hash()),
|
||||||
];
|
];
|
||||||
(transactions, bank, poh_recorder, entry_receiver)
|
let (poh_simulator, exit) = simulate_poh(record_receiver, &poh_recorder);
|
||||||
|
|
||||||
|
(
|
||||||
|
transactions,
|
||||||
|
bank,
|
||||||
|
poh_recorder,
|
||||||
|
entry_receiver,
|
||||||
|
poh_simulator,
|
||||||
|
exit,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_consume_buffered_packets() {
|
fn test_consume_buffered_packets() {
|
||||||
let ledger_path = get_tmp_ledger_path!();
|
let ledger_path = get_tmp_ledger_path!();
|
||||||
{
|
{
|
||||||
let (transactions, bank, poh_recorder, _entry_receiver) =
|
let (transactions, bank, poh_recorder, _entry_receiver, poh_simulator, exit) =
|
||||||
setup_conflicting_transactions(&ledger_path);
|
setup_conflicting_transactions(&ledger_path);
|
||||||
|
let recorder = poh_recorder.lock().unwrap().recorder();
|
||||||
let num_conflicting_transactions = transactions.len();
|
let num_conflicting_transactions = transactions.len();
|
||||||
let mut packets_vec = to_packets_chunked(&transactions, num_conflicting_transactions);
|
let mut packets_vec = to_packets_chunked(&transactions, num_conflicting_transactions);
|
||||||
assert_eq!(packets_vec.len(), 1);
|
assert_eq!(packets_vec.len(), 1);
|
||||||
@ -2468,6 +2537,7 @@ mod tests {
|
|||||||
&gossip_vote_sender,
|
&gossip_vote_sender,
|
||||||
None::<Box<dyn Fn()>>,
|
None::<Box<dyn Fn()>>,
|
||||||
None,
|
None,
|
||||||
|
&recorder,
|
||||||
);
|
);
|
||||||
assert_eq!(buffered_packets[0].1.len(), num_conflicting_transactions);
|
assert_eq!(buffered_packets[0].1.len(), num_conflicting_transactions);
|
||||||
// When the poh recorder has a bank, should process all non conflicting buffered packets.
|
// When the poh recorder has a bank, should process all non conflicting buffered packets.
|
||||||
@ -2483,6 +2553,7 @@ mod tests {
|
|||||||
&gossip_vote_sender,
|
&gossip_vote_sender,
|
||||||
None::<Box<dyn Fn()>>,
|
None::<Box<dyn Fn()>>,
|
||||||
None,
|
None,
|
||||||
|
&recorder,
|
||||||
);
|
);
|
||||||
if num_expected_unprocessed == 0 {
|
if num_expected_unprocessed == 0 {
|
||||||
assert!(buffered_packets.is_empty())
|
assert!(buffered_packets.is_empty())
|
||||||
@ -2490,6 +2561,8 @@ mod tests {
|
|||||||
assert_eq!(buffered_packets[0].1.len(), num_expected_unprocessed);
|
assert_eq!(buffered_packets[0].1.len(), num_expected_unprocessed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exit.store(true, Ordering::Relaxed);
|
||||||
|
let _ = poh_simulator.join();
|
||||||
}
|
}
|
||||||
Blockstore::destroy(&ledger_path).unwrap();
|
Blockstore::destroy(&ledger_path).unwrap();
|
||||||
}
|
}
|
||||||
@ -2498,7 +2571,7 @@ mod tests {
|
|||||||
fn test_consume_buffered_packets_interrupted() {
|
fn test_consume_buffered_packets_interrupted() {
|
||||||
let ledger_path = get_tmp_ledger_path!();
|
let ledger_path = get_tmp_ledger_path!();
|
||||||
{
|
{
|
||||||
let (transactions, bank, poh_recorder, _entry_receiver) =
|
let (transactions, bank, poh_recorder, _entry_receiver, poh_simulator, exit) =
|
||||||
setup_conflicting_transactions(&ledger_path);
|
setup_conflicting_transactions(&ledger_path);
|
||||||
let num_conflicting_transactions = transactions.len();
|
let num_conflicting_transactions = transactions.len();
|
||||||
let packets_vec = to_packets_chunked(&transactions, 1);
|
let packets_vec = to_packets_chunked(&transactions, 1);
|
||||||
@ -2526,6 +2599,7 @@ mod tests {
|
|||||||
let interrupted_iteration = 1;
|
let interrupted_iteration = 1;
|
||||||
poh_recorder.lock().unwrap().set_bank(&bank);
|
poh_recorder.lock().unwrap().set_bank(&bank);
|
||||||
let poh_recorder_ = poh_recorder.clone();
|
let poh_recorder_ = poh_recorder.clone();
|
||||||
|
let recorder = poh_recorder_.lock().unwrap().recorder();
|
||||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||||
// Start up thread to process the banks
|
// Start up thread to process the banks
|
||||||
let t_consume = Builder::new()
|
let t_consume = Builder::new()
|
||||||
@ -2540,6 +2614,7 @@ mod tests {
|
|||||||
&gossip_vote_sender,
|
&gossip_vote_sender,
|
||||||
test_fn,
|
test_fn,
|
||||||
None,
|
None,
|
||||||
|
&recorder,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check everything is correct. All indexes after `interrupted_iteration`
|
// Check everything is correct. All indexes after `interrupted_iteration`
|
||||||
@ -2573,6 +2648,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t_consume.join().unwrap();
|
t_consume.join().unwrap();
|
||||||
|
exit.store(true, Ordering::Relaxed);
|
||||||
|
let _ = poh_simulator.join();
|
||||||
}
|
}
|
||||||
Blockstore::destroy(&ledger_path).unwrap();
|
Blockstore::destroy(&ledger_path).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,80 @@ type Result<T> = std::result::Result<T, PohRecorderError>;
|
|||||||
pub type WorkingBankEntry = (Arc<Bank>, (Entry, u64));
|
pub type WorkingBankEntry = (Arc<Bank>, (Entry, u64));
|
||||||
pub type BankStart = (Arc<Bank>, Arc<Instant>);
|
pub type BankStart = (Arc<Bank>, Arc<Instant>);
|
||||||
|
|
||||||
|
pub struct Record {
|
||||||
|
pub mixin: Hash,
|
||||||
|
pub transactions: Vec<Transaction>,
|
||||||
|
pub slot: Slot,
|
||||||
|
pub sender: Sender<Result<()>>,
|
||||||
|
}
|
||||||
|
impl Record {
|
||||||
|
pub fn new(
|
||||||
|
mixin: Hash,
|
||||||
|
transactions: Vec<Transaction>,
|
||||||
|
slot: Slot,
|
||||||
|
sender: Sender<Result<()>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
mixin,
|
||||||
|
transactions,
|
||||||
|
slot,
|
||||||
|
sender,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransactionRecorder {
|
||||||
|
// shared by all users of PohRecorder
|
||||||
|
pub record_sender: Sender<Record>,
|
||||||
|
// unique to this caller
|
||||||
|
pub result_sender: Sender<Result<()>>,
|
||||||
|
pub result_receiver: Receiver<Result<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for TransactionRecorder {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
TransactionRecorder::new(self.record_sender.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionRecorder {
|
||||||
|
pub fn new(record_sender: Sender<Record>) -> Self {
|
||||||
|
let (result_sender, result_receiver) = channel();
|
||||||
|
Self {
|
||||||
|
// shared
|
||||||
|
record_sender,
|
||||||
|
// unique to this caller
|
||||||
|
result_sender,
|
||||||
|
result_receiver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn record(
|
||||||
|
&self,
|
||||||
|
bank_slot: Slot,
|
||||||
|
mixin: Hash,
|
||||||
|
transactions: Vec<Transaction>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let res = self.record_sender.send(Record::new(
|
||||||
|
mixin,
|
||||||
|
transactions,
|
||||||
|
bank_slot,
|
||||||
|
self.result_sender.clone(),
|
||||||
|
));
|
||||||
|
if res.is_err() {
|
||||||
|
// If the channel is dropped, then the validator is shutting down so return that we are hitting
|
||||||
|
// the max tick height to stop transaction processing and flush any transactions in the pipeline.
|
||||||
|
return Err(PohRecorderError::MaxHeightReached);
|
||||||
|
}
|
||||||
|
let res = self
|
||||||
|
.result_receiver
|
||||||
|
.recv_timeout(std::time::Duration::from_millis(2000));
|
||||||
|
match res {
|
||||||
|
Err(_err) => Err(PohRecorderError::MaxHeightReached),
|
||||||
|
Ok(result) => result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WorkingBank {
|
pub struct WorkingBank {
|
||||||
pub bank: Arc<Bank>,
|
pub bank: Arc<Bank>,
|
||||||
@ -86,6 +160,7 @@ pub struct PohRecorder {
|
|||||||
record_us: u64,
|
record_us: u64,
|
||||||
ticks_from_record: u64,
|
ticks_from_record: u64,
|
||||||
last_metric: Instant,
|
last_metric: Instant,
|
||||||
|
record_sender: Sender<Record>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PohRecorder {
|
impl PohRecorder {
|
||||||
@ -162,6 +237,10 @@ impl PohRecorder {
|
|||||||
self.ticks_per_slot
|
self.ticks_per_slot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn recorder(&self) -> TransactionRecorder {
|
||||||
|
TransactionRecorder::new(self.record_sender.clone())
|
||||||
|
}
|
||||||
|
|
||||||
fn is_same_fork_as_previous_leader(&self, slot: Slot) -> bool {
|
fn is_same_fork_as_previous_leader(&self, slot: Slot) -> bool {
|
||||||
(slot.saturating_sub(NUM_CONSECUTIVE_LEADER_SLOTS)..slot).any(|slot| {
|
(slot.saturating_sub(NUM_CONSECUTIVE_LEADER_SLOTS)..slot).any(|slot| {
|
||||||
// Check if the last slot Poh reset to was any of the
|
// Check if the last slot Poh reset to was any of the
|
||||||
@ -503,12 +582,13 @@ impl PohRecorder {
|
|||||||
clear_bank_signal: Option<SyncSender<bool>>,
|
clear_bank_signal: Option<SyncSender<bool>>,
|
||||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||||
poh_config: &Arc<PohConfig>,
|
poh_config: &Arc<PohConfig>,
|
||||||
) -> (Self, Receiver<WorkingBankEntry>) {
|
) -> (Self, Receiver<WorkingBankEntry>, Receiver<Record>) {
|
||||||
let poh = Arc::new(Mutex::new(Poh::new(
|
let poh = Arc::new(Mutex::new(Poh::new(
|
||||||
last_entry_hash,
|
last_entry_hash,
|
||||||
poh_config.hashes_per_tick,
|
poh_config.hashes_per_tick,
|
||||||
)));
|
)));
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
|
let (record_sender, record_receiver) = channel();
|
||||||
let (leader_first_tick_height, leader_last_tick_height, grace_ticks) =
|
let (leader_first_tick_height, leader_last_tick_height, grace_ticks) =
|
||||||
Self::compute_leader_slot_tick_heights(next_leader_slot, ticks_per_slot);
|
Self::compute_leader_slot_tick_heights(next_leader_slot, ticks_per_slot);
|
||||||
(
|
(
|
||||||
@ -539,8 +619,10 @@ impl PohRecorder {
|
|||||||
tick_overhead_us: 0,
|
tick_overhead_us: 0,
|
||||||
ticks_from_record: 0,
|
ticks_from_record: 0,
|
||||||
last_metric: Instant::now(),
|
last_metric: Instant::now(),
|
||||||
|
record_sender,
|
||||||
},
|
},
|
||||||
receiver,
|
receiver,
|
||||||
|
record_receiver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,7 +639,7 @@ impl PohRecorder {
|
|||||||
blockstore: &Arc<Blockstore>,
|
blockstore: &Arc<Blockstore>,
|
||||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||||
poh_config: &Arc<PohConfig>,
|
poh_config: &Arc<PohConfig>,
|
||||||
) -> (Self, Receiver<WorkingBankEntry>) {
|
) -> (Self, Receiver<WorkingBankEntry>, Receiver<Record>) {
|
||||||
Self::new_with_clear_signal(
|
Self::new_with_clear_signal(
|
||||||
tick_height,
|
tick_height,
|
||||||
last_entry_hash,
|
last_entry_hash,
|
||||||
@ -609,7 +691,7 @@ mod tests {
|
|||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
|
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -636,7 +718,7 @@ mod tests {
|
|||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
|
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -662,7 +744,7 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
0,
|
0,
|
||||||
@ -690,7 +772,7 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -726,7 +808,7 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -777,7 +859,7 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -826,7 +908,7 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -864,7 +946,7 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -906,7 +988,7 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -952,7 +1034,7 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -996,7 +1078,7 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -1033,7 +1115,7 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
0,
|
0,
|
||||||
@ -1060,7 +1142,7 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
0,
|
0,
|
||||||
@ -1088,7 +1170,7 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let blockstore = Blockstore::open(&ledger_path)
|
let blockstore = Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
0,
|
0,
|
||||||
@ -1121,7 +1203,7 @@ mod tests {
|
|||||||
.expect("Expected to be able to open database ledger");
|
.expect("Expected to be able to open database ledger");
|
||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
0,
|
0,
|
||||||
@ -1155,18 +1237,19 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let (sender, receiver) = sync_channel(1);
|
let (sender, receiver) = sync_channel(1);
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new_with_clear_signal(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) =
|
||||||
0,
|
PohRecorder::new_with_clear_signal(
|
||||||
Hash::default(),
|
0,
|
||||||
0,
|
Hash::default(),
|
||||||
None,
|
0,
|
||||||
bank.ticks_per_slot(),
|
None,
|
||||||
&Pubkey::default(),
|
bank.ticks_per_slot(),
|
||||||
&Arc::new(blockstore),
|
&Pubkey::default(),
|
||||||
Some(sender),
|
&Arc::new(blockstore),
|
||||||
&Arc::new(LeaderScheduleCache::default()),
|
Some(sender),
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(LeaderScheduleCache::default()),
|
||||||
);
|
&Arc::new(PohConfig::default()),
|
||||||
|
);
|
||||||
poh_recorder.set_bank(&bank);
|
poh_recorder.set_bank(&bank);
|
||||||
poh_recorder.clear_bank();
|
poh_recorder.clear_bank();
|
||||||
assert!(receiver.try_recv().is_ok());
|
assert!(receiver.try_recv().is_ok());
|
||||||
@ -1189,7 +1272,7 @@ mod tests {
|
|||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
|
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -1238,7 +1321,7 @@ mod tests {
|
|||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
|
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -1300,7 +1383,7 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -1429,7 +1512,7 @@ mod tests {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let prev_hash = bank.last_blockhash();
|
let prev_hash = bank.last_blockhash();
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
prev_hash,
|
prev_hash,
|
||||||
0,
|
0,
|
||||||
@ -1497,7 +1580,7 @@ mod tests {
|
|||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let genesis_hash = bank.last_blockhash();
|
let genesis_hash = bank.last_blockhash();
|
||||||
|
|
||||||
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
bank.last_blockhash(),
|
bank.last_blockhash(),
|
||||||
0,
|
0,
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
//! The `poh_service` module implements a service that records the passing of
|
//! The `poh_service` module implements a service that records the passing of
|
||||||
//! "ticks", a measure of time in the PoH stream
|
//! "ticks", a measure of time in the PoH stream
|
||||||
use crate::poh_recorder::PohRecorder;
|
use crate::poh_recorder::{PohRecorder, Record};
|
||||||
|
use solana_ledger::poh::Poh;
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_sdk::poh_config::PohConfig;
|
use solana_sdk::poh_config::PohConfig;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{mpsc::Receiver, Arc, Mutex};
|
||||||
use std::thread::{self, sleep, Builder, JoinHandle};
|
use std::thread::{self, sleep, Builder, JoinHandle};
|
||||||
use std::time::Instant;
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
pub struct PohService {
|
pub struct PohService {
|
||||||
tick_producer: JoinHandle<()>,
|
tick_producer: JoinHandle<()>,
|
||||||
@ -24,6 +25,54 @@ pub const DEFAULT_PINNED_CPU_CORE: usize = 0;
|
|||||||
|
|
||||||
const TARGET_SLOT_ADJUSTMENT_NS: u64 = 50_000_000;
|
const TARGET_SLOT_ADJUSTMENT_NS: u64 = 50_000_000;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PohTiming {
|
||||||
|
num_ticks: u64,
|
||||||
|
num_hashes: u64,
|
||||||
|
total_sleep_us: u64,
|
||||||
|
total_lock_time_ns: u64,
|
||||||
|
total_hash_time_ns: u64,
|
||||||
|
total_tick_time_ns: u64,
|
||||||
|
last_metric: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PohTiming {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
num_ticks: 0,
|
||||||
|
num_hashes: 0,
|
||||||
|
total_sleep_us: 0,
|
||||||
|
total_lock_time_ns: 0,
|
||||||
|
total_hash_time_ns: 0,
|
||||||
|
total_tick_time_ns: 0,
|
||||||
|
last_metric: Instant::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn report(&mut self, ticks_per_slot: u64) {
|
||||||
|
if self.last_metric.elapsed().as_millis() > 1000 {
|
||||||
|
let elapsed_us = self.last_metric.elapsed().as_micros() as u64;
|
||||||
|
let us_per_slot = (elapsed_us * ticks_per_slot) / self.num_ticks;
|
||||||
|
datapoint_info!(
|
||||||
|
"poh-service",
|
||||||
|
("ticks", self.num_ticks as i64, i64),
|
||||||
|
("hashes", self.num_hashes as i64, i64),
|
||||||
|
("elapsed_us", us_per_slot, i64),
|
||||||
|
("total_sleep_us", self.total_sleep_us, i64),
|
||||||
|
("total_tick_time_us", self.total_tick_time_ns / 1000, i64),
|
||||||
|
("total_lock_time_us", self.total_lock_time_ns / 1000, i64),
|
||||||
|
("total_hash_time_us", self.total_hash_time_ns / 1000, i64),
|
||||||
|
);
|
||||||
|
self.total_sleep_us = 0;
|
||||||
|
self.num_ticks = 0;
|
||||||
|
self.num_hashes = 0;
|
||||||
|
self.total_tick_time_ns = 0;
|
||||||
|
self.total_lock_time_ns = 0;
|
||||||
|
self.total_hash_time_ns = 0;
|
||||||
|
self.last_metric = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PohService {
|
impl PohService {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
poh_recorder: Arc<Mutex<PohRecorder>>,
|
poh_recorder: Arc<Mutex<PohRecorder>>,
|
||||||
@ -32,6 +81,7 @@ impl PohService {
|
|||||||
ticks_per_slot: u64,
|
ticks_per_slot: u64,
|
||||||
pinned_cpu_core: usize,
|
pinned_cpu_core: usize,
|
||||||
hashes_per_batch: u64,
|
hashes_per_batch: u64,
|
||||||
|
record_receiver: Receiver<Record>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let poh_exit_ = poh_exit.clone();
|
let poh_exit_ = poh_exit.clone();
|
||||||
let poh_config = poh_config.clone();
|
let poh_config = poh_config.clone();
|
||||||
@ -41,12 +91,18 @@ impl PohService {
|
|||||||
solana_sys_tuner::request_realtime_poh();
|
solana_sys_tuner::request_realtime_poh();
|
||||||
if poh_config.hashes_per_tick.is_none() {
|
if poh_config.hashes_per_tick.is_none() {
|
||||||
if poh_config.target_tick_count.is_none() {
|
if poh_config.target_tick_count.is_none() {
|
||||||
Self::sleepy_tick_producer(poh_recorder, &poh_config, &poh_exit_);
|
Self::sleepy_tick_producer(
|
||||||
|
poh_recorder,
|
||||||
|
&poh_config,
|
||||||
|
&poh_exit_,
|
||||||
|
record_receiver,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
Self::short_lived_sleepy_tick_producer(
|
Self::short_lived_sleepy_tick_producer(
|
||||||
poh_recorder,
|
poh_recorder,
|
||||||
&poh_config,
|
&poh_config,
|
||||||
&poh_exit_,
|
&poh_exit_,
|
||||||
|
record_receiver,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -69,6 +125,7 @@ impl PohService {
|
|||||||
poh_config.target_tick_duration.as_nanos() as u64 - adjustment_per_tick,
|
poh_config.target_tick_duration.as_nanos() as u64 - adjustment_per_tick,
|
||||||
ticks_per_slot,
|
ticks_per_slot,
|
||||||
hashes_per_batch,
|
hashes_per_batch,
|
||||||
|
record_receiver,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
poh_exit_.store(true, Ordering::Relaxed);
|
poh_exit_.store(true, Ordering::Relaxed);
|
||||||
@ -82,20 +139,53 @@ impl PohService {
|
|||||||
poh_recorder: Arc<Mutex<PohRecorder>>,
|
poh_recorder: Arc<Mutex<PohRecorder>>,
|
||||||
poh_config: &PohConfig,
|
poh_config: &PohConfig,
|
||||||
poh_exit: &AtomicBool,
|
poh_exit: &AtomicBool,
|
||||||
|
record_receiver: Receiver<Record>,
|
||||||
) {
|
) {
|
||||||
while !poh_exit.load(Ordering::Relaxed) {
|
while !poh_exit.load(Ordering::Relaxed) {
|
||||||
|
Self::read_record_receiver_and_process(
|
||||||
|
&poh_recorder,
|
||||||
|
&record_receiver,
|
||||||
|
Duration::from_millis(0),
|
||||||
|
);
|
||||||
sleep(poh_config.target_tick_duration);
|
sleep(poh_config.target_tick_duration);
|
||||||
poh_recorder.lock().unwrap().tick();
|
poh_recorder.lock().unwrap().tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_record_receiver_and_process(
|
||||||
|
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
||||||
|
record_receiver: &Receiver<Record>,
|
||||||
|
timeout: Duration,
|
||||||
|
) {
|
||||||
|
let record = record_receiver.recv_timeout(timeout);
|
||||||
|
if let Ok(record) = record {
|
||||||
|
if record
|
||||||
|
.sender
|
||||||
|
.send(poh_recorder.lock().unwrap().record(
|
||||||
|
record.slot,
|
||||||
|
record.mixin,
|
||||||
|
record.transactions,
|
||||||
|
))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
panic!("Error returning mixin hash");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn short_lived_sleepy_tick_producer(
|
fn short_lived_sleepy_tick_producer(
|
||||||
poh_recorder: Arc<Mutex<PohRecorder>>,
|
poh_recorder: Arc<Mutex<PohRecorder>>,
|
||||||
poh_config: &PohConfig,
|
poh_config: &PohConfig,
|
||||||
poh_exit: &AtomicBool,
|
poh_exit: &AtomicBool,
|
||||||
|
record_receiver: Receiver<Record>,
|
||||||
) {
|
) {
|
||||||
let mut warned = false;
|
let mut warned = false;
|
||||||
for _ in 0..poh_config.target_tick_count.unwrap() {
|
for _ in 0..poh_config.target_tick_count.unwrap() {
|
||||||
|
Self::read_record_receiver_and_process(
|
||||||
|
&poh_recorder,
|
||||||
|
&record_receiver,
|
||||||
|
Duration::from_millis(0),
|
||||||
|
);
|
||||||
sleep(poh_config.target_tick_duration);
|
sleep(poh_config.target_tick_duration);
|
||||||
poh_recorder.lock().unwrap().tick();
|
poh_recorder.lock().unwrap().tick();
|
||||||
if poh_exit.load(Ordering::Relaxed) && !warned {
|
if poh_exit.load(Ordering::Relaxed) && !warned {
|
||||||
@ -105,78 +195,121 @@ impl PohService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn record_or_hash(
|
||||||
|
next_record: &mut Option<Record>,
|
||||||
|
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
||||||
|
timing: &mut PohTiming,
|
||||||
|
record_receiver: &Receiver<Record>,
|
||||||
|
hashes_per_batch: u64,
|
||||||
|
poh: &Arc<Mutex<Poh>>,
|
||||||
|
) -> bool {
|
||||||
|
match next_record.take() {
|
||||||
|
Some(mut record) => {
|
||||||
|
// received message to record
|
||||||
|
// so, record for as long as we have queued up record requests
|
||||||
|
let mut lock_time = Measure::start("lock");
|
||||||
|
let mut poh_recorder_l = poh_recorder.lock().unwrap();
|
||||||
|
lock_time.stop();
|
||||||
|
timing.total_lock_time_ns += lock_time.as_ns();
|
||||||
|
loop {
|
||||||
|
let res = poh_recorder_l.record(
|
||||||
|
record.slot,
|
||||||
|
record.mixin,
|
||||||
|
std::mem::take(&mut record.transactions),
|
||||||
|
);
|
||||||
|
let _ = record.sender.send(res); // what do we do on failure here? Ignore for now.
|
||||||
|
timing.num_hashes += 1; // note: may have also ticked inside record
|
||||||
|
|
||||||
|
let new_record_result = record_receiver.try_recv();
|
||||||
|
match new_record_result {
|
||||||
|
Ok(new_record) => {
|
||||||
|
// we already have second request to record, so record again while we still have the mutex
|
||||||
|
record = new_record;
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// PohRecorder.record would have ticked if it needed to, so should_tick will be false
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// did not receive instructions to record, so hash until we notice we've been asked to record (or we need to tick) and then remember what to record
|
||||||
|
let mut lock_time = Measure::start("lock");
|
||||||
|
let mut poh_l = poh.lock().unwrap();
|
||||||
|
lock_time.stop();
|
||||||
|
timing.total_lock_time_ns += lock_time.as_ns();
|
||||||
|
loop {
|
||||||
|
timing.num_hashes += hashes_per_batch;
|
||||||
|
let mut hash_time = Measure::start("hash");
|
||||||
|
let should_tick = poh_l.hash(hashes_per_batch);
|
||||||
|
hash_time.stop();
|
||||||
|
timing.total_hash_time_ns += hash_time.as_ns();
|
||||||
|
if should_tick {
|
||||||
|
return true; // nothing else can be done. tick required.
|
||||||
|
}
|
||||||
|
// check to see if a record request has been sent
|
||||||
|
let get_again = record_receiver.try_recv();
|
||||||
|
match get_again {
|
||||||
|
Ok(record) => {
|
||||||
|
// remember the record we just received as the next record to occur
|
||||||
|
*next_record = Some(record);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
false // should_tick = false for all code that reaches here
|
||||||
|
}
|
||||||
|
|
||||||
fn tick_producer(
|
fn tick_producer(
|
||||||
poh_recorder: Arc<Mutex<PohRecorder>>,
|
poh_recorder: Arc<Mutex<PohRecorder>>,
|
||||||
poh_exit: &AtomicBool,
|
poh_exit: &AtomicBool,
|
||||||
target_tick_ns: u64,
|
target_tick_ns: u64,
|
||||||
ticks_per_slot: u64,
|
ticks_per_slot: u64,
|
||||||
hashes_per_batch: u64,
|
hashes_per_batch: u64,
|
||||||
|
record_receiver: Receiver<Record>,
|
||||||
) {
|
) {
|
||||||
let poh = poh_recorder.lock().unwrap().poh.clone();
|
let poh = poh_recorder.lock().unwrap().poh.clone();
|
||||||
let mut now = Instant::now();
|
let mut now = Instant::now();
|
||||||
let mut last_metric = Instant::now();
|
let mut timing = PohTiming::new();
|
||||||
let mut num_ticks = 0;
|
let mut next_record = None;
|
||||||
let mut num_hashes = 0;
|
|
||||||
let mut total_sleep_us = 0;
|
|
||||||
let mut total_lock_time_ns = 0;
|
|
||||||
let mut total_hash_time_ns = 0;
|
|
||||||
let mut total_tick_time_ns = 0;
|
|
||||||
loop {
|
loop {
|
||||||
num_hashes += hashes_per_batch;
|
let should_tick = Self::record_or_hash(
|
||||||
let should_tick = {
|
&mut next_record,
|
||||||
let mut lock_time = Measure::start("lock");
|
&poh_recorder,
|
||||||
let mut poh_l = poh.lock().unwrap();
|
&mut timing,
|
||||||
lock_time.stop();
|
&record_receiver,
|
||||||
total_lock_time_ns += lock_time.as_ns();
|
hashes_per_batch,
|
||||||
let mut hash_time = Measure::start("hash");
|
&poh,
|
||||||
let r = poh_l.hash(hashes_per_batch);
|
);
|
||||||
hash_time.stop();
|
|
||||||
total_hash_time_ns += hash_time.as_ns();
|
|
||||||
r
|
|
||||||
};
|
|
||||||
if should_tick {
|
if should_tick {
|
||||||
// Lock PohRecorder only for the final hash...
|
// Lock PohRecorder only for the final hash. record_or_hash will lock PohRecorder for record calls but not for hashing.
|
||||||
{
|
{
|
||||||
let mut lock_time = Measure::start("lock");
|
let mut lock_time = Measure::start("lock");
|
||||||
let mut poh_recorder_l = poh_recorder.lock().unwrap();
|
let mut poh_recorder_l = poh_recorder.lock().unwrap();
|
||||||
lock_time.stop();
|
lock_time.stop();
|
||||||
total_lock_time_ns += lock_time.as_ns();
|
timing.total_lock_time_ns += lock_time.as_ns();
|
||||||
let mut tick_time = Measure::start("tick");
|
let mut tick_time = Measure::start("tick");
|
||||||
poh_recorder_l.tick();
|
poh_recorder_l.tick();
|
||||||
tick_time.stop();
|
tick_time.stop();
|
||||||
total_tick_time_ns += tick_time.as_ns();
|
timing.total_tick_time_ns += tick_time.as_ns();
|
||||||
}
|
}
|
||||||
num_ticks += 1;
|
timing.num_ticks += 1;
|
||||||
let elapsed_ns = now.elapsed().as_nanos() as u64;
|
let elapsed_ns = now.elapsed().as_nanos() as u64;
|
||||||
// sleep is not accurate enough to get a predictable time.
|
// sleep is not accurate enough to get a predictable time.
|
||||||
// Kernel can not schedule the thread for a while.
|
// Kernel can not schedule the thread for a while.
|
||||||
while (now.elapsed().as_nanos() as u64) < target_tick_ns {
|
while (now.elapsed().as_nanos() as u64) < target_tick_ns {
|
||||||
std::hint::spin_loop();
|
std::hint::spin_loop();
|
||||||
}
|
}
|
||||||
total_sleep_us += (now.elapsed().as_nanos() as u64 - elapsed_ns) / 1000;
|
timing.total_sleep_us += (now.elapsed().as_nanos() as u64 - elapsed_ns) / 1000;
|
||||||
now = Instant::now();
|
now = Instant::now();
|
||||||
|
|
||||||
if last_metric.elapsed().as_millis() > 1000 {
|
timing.report(ticks_per_slot);
|
||||||
let elapsed_us = last_metric.elapsed().as_micros() as u64;
|
|
||||||
let us_per_slot = (elapsed_us * ticks_per_slot) / num_ticks;
|
|
||||||
datapoint_info!(
|
|
||||||
"poh-service",
|
|
||||||
("ticks", num_ticks as i64, i64),
|
|
||||||
("hashes", num_hashes as i64, i64),
|
|
||||||
("elapsed_us", us_per_slot, i64),
|
|
||||||
("total_sleep_us", total_sleep_us, i64),
|
|
||||||
("total_tick_time_us", total_tick_time_ns / 1000, i64),
|
|
||||||
("total_lock_time_us", total_lock_time_ns / 1000, i64),
|
|
||||||
("total_hash_time_us", total_hash_time_ns / 1000, i64),
|
|
||||||
);
|
|
||||||
total_sleep_us = 0;
|
|
||||||
num_ticks = 0;
|
|
||||||
num_hashes = 0;
|
|
||||||
total_tick_time_ns = 0;
|
|
||||||
total_lock_time_ns = 0;
|
|
||||||
total_hash_time_ns = 0;
|
|
||||||
last_metric = Instant::now();
|
|
||||||
}
|
|
||||||
if poh_exit.load(Ordering::Relaxed) {
|
if poh_exit.load(Ordering::Relaxed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -225,7 +358,7 @@ mod tests {
|
|||||||
target_tick_duration,
|
target_tick_duration,
|
||||||
target_tick_count: None,
|
target_tick_count: None,
|
||||||
});
|
});
|
||||||
let (poh_recorder, entry_receiver) = PohRecorder::new(
|
let (poh_recorder, entry_receiver, record_receiver) = PohRecorder::new(
|
||||||
bank.tick_height(),
|
bank.tick_height(),
|
||||||
prev_hash,
|
prev_hash,
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
@ -305,6 +438,7 @@ mod tests {
|
|||||||
0,
|
0,
|
||||||
DEFAULT_PINNED_CPU_CORE,
|
DEFAULT_PINNED_CPU_CORE,
|
||||||
hashes_per_batch,
|
hashes_per_batch,
|
||||||
|
record_receiver,
|
||||||
);
|
);
|
||||||
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
||||||
|
|
||||||
|
@ -801,7 +801,7 @@ mod test {
|
|||||||
);
|
);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
|
|
||||||
let (poh_recorder, _entry_receiver) = PohRecorder::new(
|
let (poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
|
||||||
0,
|
0,
|
||||||
bank.last_blockhash(),
|
bank.last_blockhash(),
|
||||||
0,
|
0,
|
||||||
|
@ -486,24 +486,25 @@ impl Validator {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let poh_config = Arc::new(genesis_config.poh_config.clone());
|
let poh_config = Arc::new(genesis_config.poh_config.clone());
|
||||||
let (mut poh_recorder, entry_receiver) = PohRecorder::new_with_clear_signal(
|
let (mut poh_recorder, entry_receiver, record_receiver) =
|
||||||
bank.tick_height(),
|
PohRecorder::new_with_clear_signal(
|
||||||
bank.last_blockhash(),
|
bank.tick_height(),
|
||||||
bank.slot(),
|
bank.last_blockhash(),
|
||||||
leader_schedule_cache.next_leader_slot(
|
|
||||||
&id,
|
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
&bank,
|
leader_schedule_cache.next_leader_slot(
|
||||||
Some(&blockstore),
|
&id,
|
||||||
GRACE_TICKS_FACTOR * MAX_GRACE_SLOTS,
|
bank.slot(),
|
||||||
),
|
&bank,
|
||||||
bank.ticks_per_slot(),
|
Some(&blockstore),
|
||||||
&id,
|
GRACE_TICKS_FACTOR * MAX_GRACE_SLOTS,
|
||||||
&blockstore,
|
),
|
||||||
blockstore.new_shreds_signals.first().cloned(),
|
bank.ticks_per_slot(),
|
||||||
&leader_schedule_cache,
|
&id,
|
||||||
&poh_config,
|
&blockstore,
|
||||||
);
|
blockstore.new_shreds_signals.first().cloned(),
|
||||||
|
&leader_schedule_cache,
|
||||||
|
&poh_config,
|
||||||
|
);
|
||||||
if config.snapshot_config.is_some() {
|
if config.snapshot_config.is_some() {
|
||||||
poh_recorder.set_bank(&bank);
|
poh_recorder.set_bank(&bank);
|
||||||
}
|
}
|
||||||
@ -644,6 +645,7 @@ impl Validator {
|
|||||||
bank.ticks_per_slot(),
|
bank.ticks_per_slot(),
|
||||||
config.poh_pinned_cpu_core,
|
config.poh_pinned_cpu_core,
|
||||||
config.poh_hashes_per_batch,
|
config.poh_hashes_per_batch,
|
||||||
|
record_receiver,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
blockstore.new_shreds_signals.len(),
|
blockstore.new_shreds_signals.len(),
|
||||||
|
Reference in New Issue
Block a user