Wallclock BankingStage Throttle (#15731)
This commit is contained in:
parent
60e5fd11c9
commit
c1ba265dd9
|
@ -81,6 +81,7 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
||||||
bencher.iter(move || {
|
bencher.iter(move || {
|
||||||
let _ignored = BankingStage::consume_buffered_packets(
|
let _ignored = BankingStage::consume_buffered_packets(
|
||||||
&my_pubkey,
|
&my_pubkey,
|
||||||
|
std::u128::MAX,
|
||||||
&poh_recorder,
|
&poh_recorder,
|
||||||
&mut packets,
|
&mut packets,
|
||||||
None,
|
None,
|
||||||
|
@ -154,6 +155,9 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
|
||||||
|
|
||||||
let (verified_sender, verified_receiver) = unbounded();
|
let (verified_sender, verified_receiver) = unbounded();
|
||||||
let (vote_sender, vote_receiver) = unbounded();
|
let (vote_sender, vote_receiver) = unbounded();
|
||||||
|
let mut bank = Bank::new(&genesis_config);
|
||||||
|
// Allow arbitrary transaction processing time for the purposes of this bench
|
||||||
|
bank.ns_per_slot = std::u128::MAX;
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
|
|
||||||
debug!("threads: {} txs: {}", num_threads, txes);
|
debug!("threads: {} txs: {}", num_threads, txes);
|
||||||
|
|
|
@ -159,9 +159,9 @@ pub struct BankingStage {
|
||||||
bank_thread_hdls: Vec<JoinHandle<()>>,
|
bank_thread_hdls: Vec<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum BufferedPacketsDecision {
|
pub enum BufferedPacketsDecision {
|
||||||
Consume,
|
Consume(u128),
|
||||||
Forward,
|
Forward,
|
||||||
ForwardAndHold,
|
ForwardAndHold,
|
||||||
Hold,
|
Hold,
|
||||||
|
@ -288,6 +288,7 @@ impl BankingStage {
|
||||||
|
|
||||||
pub fn consume_buffered_packets(
|
pub fn consume_buffered_packets(
|
||||||
my_pubkey: &Pubkey,
|
my_pubkey: &Pubkey,
|
||||||
|
max_tx_ingestion_ns: u128,
|
||||||
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
||||||
buffered_packets: &mut UnprocessedPackets,
|
buffered_packets: &mut UnprocessedPackets,
|
||||||
transaction_status_sender: Option<TransactionStatusSender>,
|
transaction_status_sender: Option<TransactionStatusSender>,
|
||||||
|
@ -316,18 +317,24 @@ impl BankingStage {
|
||||||
new_unprocessed_indexes,
|
new_unprocessed_indexes,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let bank = poh_recorder.lock().unwrap().bank();
|
let bank_start = poh_recorder.lock().unwrap().bank_start();
|
||||||
if let Some(bank) = bank {
|
if let Some((bank, bank_creation_time)) = bank_start {
|
||||||
let (processed, verified_txs_len, new_unprocessed_indexes) =
|
let (processed, verified_txs_len, new_unprocessed_indexes) =
|
||||||
Self::process_received_packets(
|
Self::process_packets_transactions(
|
||||||
&bank,
|
&bank,
|
||||||
|
&bank_creation_time,
|
||||||
&poh_recorder,
|
&poh_recorder,
|
||||||
&msgs,
|
&msgs,
|
||||||
original_unprocessed_indexes.to_owned(),
|
original_unprocessed_indexes.to_owned(),
|
||||||
transaction_status_sender.clone(),
|
transaction_status_sender.clone(),
|
||||||
gossip_vote_sender,
|
gossip_vote_sender,
|
||||||
);
|
);
|
||||||
if processed < verified_txs_len {
|
if processed < verified_txs_len
|
||||||
|
|| !Bank::should_bank_still_be_processing_txs(
|
||||||
|
&bank_creation_time,
|
||||||
|
max_tx_ingestion_ns,
|
||||||
|
)
|
||||||
|
{
|
||||||
reached_end_of_slot =
|
reached_end_of_slot =
|
||||||
Some((poh_recorder.lock().unwrap().next_slot_leader(), bank));
|
Some((poh_recorder.lock().unwrap().next_slot_leader(), bank));
|
||||||
}
|
}
|
||||||
|
@ -380,7 +387,7 @@ impl BankingStage {
|
||||||
fn consume_or_forward_packets(
|
fn consume_or_forward_packets(
|
||||||
my_pubkey: &Pubkey,
|
my_pubkey: &Pubkey,
|
||||||
leader_pubkey: Option<Pubkey>,
|
leader_pubkey: Option<Pubkey>,
|
||||||
bank_is_available: bool,
|
bank_still_processing_txs: Option<&Arc<Bank>>,
|
||||||
would_be_leader: bool,
|
would_be_leader: bool,
|
||||||
would_be_leader_shortly: bool,
|
would_be_leader_shortly: bool,
|
||||||
) -> BufferedPacketsDecision {
|
) -> BufferedPacketsDecision {
|
||||||
|
@ -389,9 +396,9 @@ impl BankingStage {
|
||||||
BufferedPacketsDecision::Hold,
|
BufferedPacketsDecision::Hold,
|
||||||
// else process the packets
|
// else process the packets
|
||||||
|x| {
|
|x| {
|
||||||
if bank_is_available {
|
if let Some(bank) = bank_still_processing_txs {
|
||||||
// If the bank is available, this node is the leader
|
// If the bank is available, this node is the leader
|
||||||
BufferedPacketsDecision::Consume
|
BufferedPacketsDecision::Consume(bank.ns_per_slot)
|
||||||
} else if would_be_leader_shortly {
|
} else if would_be_leader_shortly {
|
||||||
// If the node will be the leader soon, hold the packets for now
|
// If the node will be the leader soon, hold the packets for now
|
||||||
BufferedPacketsDecision::Hold
|
BufferedPacketsDecision::Hold
|
||||||
|
@ -422,11 +429,18 @@ impl BankingStage {
|
||||||
gossip_vote_sender: &ReplayVoteSender,
|
gossip_vote_sender: &ReplayVoteSender,
|
||||||
banking_stage_stats: &BankingStageStats,
|
banking_stage_stats: &BankingStageStats,
|
||||||
) -> BufferedPacketsDecision {
|
) -> BufferedPacketsDecision {
|
||||||
let (leader_at_slot_offset, poh_has_bank, would_be_leader, would_be_leader_shortly) = {
|
let bank_start;
|
||||||
|
let (
|
||||||
|
leader_at_slot_offset,
|
||||||
|
bank_still_processing_txs,
|
||||||
|
would_be_leader,
|
||||||
|
would_be_leader_shortly,
|
||||||
|
) = {
|
||||||
let poh = poh_recorder.lock().unwrap();
|
let poh = poh_recorder.lock().unwrap();
|
||||||
|
bank_start = poh.bank_start();
|
||||||
(
|
(
|
||||||
poh.leader_after_n_slots(FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET),
|
poh.leader_after_n_slots(FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET),
|
||||||
poh.has_bank(),
|
PohRecorder::get_bank_still_processing_txs(&bank_start),
|
||||||
poh.would_be_leader(HOLD_TRANSACTIONS_SLOT_OFFSET * DEFAULT_TICKS_PER_SLOT),
|
poh.would_be_leader(HOLD_TRANSACTIONS_SLOT_OFFSET * DEFAULT_TICKS_PER_SLOT),
|
||||||
poh.would_be_leader(
|
poh.would_be_leader(
|
||||||
(FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET - 1) * DEFAULT_TICKS_PER_SLOT,
|
(FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET - 1) * DEFAULT_TICKS_PER_SLOT,
|
||||||
|
@ -437,15 +451,16 @@ impl BankingStage {
|
||||||
let decision = Self::consume_or_forward_packets(
|
let decision = Self::consume_or_forward_packets(
|
||||||
my_pubkey,
|
my_pubkey,
|
||||||
leader_at_slot_offset,
|
leader_at_slot_offset,
|
||||||
poh_has_bank,
|
bank_still_processing_txs,
|
||||||
would_be_leader,
|
would_be_leader,
|
||||||
would_be_leader_shortly,
|
would_be_leader_shortly,
|
||||||
);
|
);
|
||||||
|
|
||||||
match decision {
|
match decision {
|
||||||
BufferedPacketsDecision::Consume => {
|
BufferedPacketsDecision::Consume(max_tx_ingestion_ns) => {
|
||||||
Self::consume_buffered_packets(
|
Self::consume_buffered_packets(
|
||||||
my_pubkey,
|
my_pubkey,
|
||||||
|
max_tx_ingestion_ns,
|
||||||
poh_recorder,
|
poh_recorder,
|
||||||
buffered_packets,
|
buffered_packets,
|
||||||
transaction_status_sender,
|
transaction_status_sender,
|
||||||
|
@ -545,8 +560,8 @@ impl BankingStage {
|
||||||
&gossip_vote_sender,
|
&gossip_vote_sender,
|
||||||
&banking_stage_stats,
|
&banking_stage_stats,
|
||||||
);
|
);
|
||||||
if decision == BufferedPacketsDecision::Hold
|
if matches!(decision, BufferedPacketsDecision::Hold)
|
||||||
|| decision == BufferedPacketsDecision::ForwardAndHold
|
|| matches!(decision, BufferedPacketsDecision::ForwardAndHold)
|
||||||
{
|
{
|
||||||
// If we are waiting on a new bank,
|
// If we are waiting on a new bank,
|
||||||
// check the receiver for more transactions/for exiting
|
// check the receiver for more transactions/for exiting
|
||||||
|
@ -829,6 +844,7 @@ impl BankingStage {
|
||||||
/// than the total number if max PoH height was reached and the bank halted
|
/// than the total number if max PoH height was reached and the bank halted
|
||||||
fn process_transactions(
|
fn process_transactions(
|
||||||
bank: &Arc<Bank>,
|
bank: &Arc<Bank>,
|
||||||
|
bank_creation_time: &Instant,
|
||||||
transactions: &[Transaction],
|
transactions: &[Transaction],
|
||||||
poh: &Arc<Mutex<PohRecorder>>,
|
poh: &Arc<Mutex<PohRecorder>>,
|
||||||
transaction_status_sender: Option<TransactionStatusSender>,
|
transaction_status_sender: Option<TransactionStatusSender>,
|
||||||
|
@ -855,7 +871,13 @@ impl BankingStage {
|
||||||
// Add the retryable txs (transactions that errored in a way that warrants a retry)
|
// Add the retryable txs (transactions that errored in a way that warrants a retry)
|
||||||
// to the list of unprocessed txs.
|
// to the list of unprocessed txs.
|
||||||
unprocessed_txs.extend_from_slice(&retryable_txs_in_chunk);
|
unprocessed_txs.extend_from_slice(&retryable_txs_in_chunk);
|
||||||
if let Err(PohRecorderError::MaxHeightReached) = result {
|
|
||||||
|
// If `bank_creation_time` is None, it's a test so ignore the option so
|
||||||
|
// allow processing
|
||||||
|
let should_bank_still_be_processing_txs =
|
||||||
|
Bank::should_bank_still_be_processing_txs(bank_creation_time, bank.ns_per_slot);
|
||||||
|
match (result, should_bank_still_be_processing_txs) {
|
||||||
|
(Err(PohRecorderError::MaxHeightReached), _) | (_, false) => {
|
||||||
info!(
|
info!(
|
||||||
"process transactions: max height reached slot: {} height: {}",
|
"process transactions: max height reached slot: {} height: {}",
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
|
@ -867,6 +889,8 @@ impl BankingStage {
|
||||||
unprocessed_txs.extend(chunk_end..transactions.len());
|
unprocessed_txs.extend(chunk_end..transactions.len());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
// Don't exit early on any other type of error, continue processing...
|
// Don't exit early on any other type of error, continue processing...
|
||||||
chunk_start = chunk_end;
|
chunk_start = chunk_end;
|
||||||
}
|
}
|
||||||
|
@ -990,8 +1014,9 @@ impl BankingStage {
|
||||||
Self::filter_valid_transaction_indexes(&result, transaction_to_packet_indexes)
|
Self::filter_valid_transaction_indexes(&result, transaction_to_packet_indexes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_received_packets(
|
fn process_packets_transactions(
|
||||||
bank: &Arc<Bank>,
|
bank: &Arc<Bank>,
|
||||||
|
bank_creation_time: &Instant,
|
||||||
poh: &Arc<Mutex<PohRecorder>>,
|
poh: &Arc<Mutex<PohRecorder>>,
|
||||||
msgs: &Packets,
|
msgs: &Packets,
|
||||||
packet_indexes: Vec<usize>,
|
packet_indexes: Vec<usize>,
|
||||||
|
@ -1013,6 +1038,7 @@ impl BankingStage {
|
||||||
|
|
||||||
let (processed, unprocessed_tx_indexes) = Self::process_transactions(
|
let (processed, unprocessed_tx_indexes) = Self::process_transactions(
|
||||||
bank,
|
bank,
|
||||||
|
bank_creation_time,
|
||||||
&transactions,
|
&transactions,
|
||||||
poh,
|
poh,
|
||||||
transaction_status_sender,
|
transaction_status_sender,
|
||||||
|
@ -1120,7 +1146,7 @@ impl BankingStage {
|
||||||
id,
|
id,
|
||||||
);
|
);
|
||||||
inc_new_counter_debug!("banking_stage-transactions_received", count);
|
inc_new_counter_debug!("banking_stage-transactions_received", count);
|
||||||
let mut proc_start = Measure::start("process_received_packets_process");
|
let mut proc_start = Measure::start("process_packets_transactions_process");
|
||||||
let mut new_tx_count = 0;
|
let mut new_tx_count = 0;
|
||||||
|
|
||||||
let mut mms_iter = mms.into_iter();
|
let mut mms_iter = mms.into_iter();
|
||||||
|
@ -1128,8 +1154,8 @@ impl BankingStage {
|
||||||
let mut newly_buffered_packets_count = 0;
|
let mut newly_buffered_packets_count = 0;
|
||||||
while let Some(msgs) = mms_iter.next() {
|
while let Some(msgs) = mms_iter.next() {
|
||||||
let packet_indexes = Self::generate_packet_indexes(&msgs.packets);
|
let packet_indexes = Self::generate_packet_indexes(&msgs.packets);
|
||||||
let bank = poh.lock().unwrap().bank();
|
let bank_start = poh.lock().unwrap().bank_start();
|
||||||
if bank.is_none() {
|
if PohRecorder::get_bank_still_processing_txs(&bank_start).is_none() {
|
||||||
Self::push_unprocessed(
|
Self::push_unprocessed(
|
||||||
buffered_packets,
|
buffered_packets,
|
||||||
msgs,
|
msgs,
|
||||||
|
@ -1141,10 +1167,12 @@ impl BankingStage {
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let bank = bank.unwrap();
|
let (bank, bank_creation_time) = bank_start.unwrap();
|
||||||
|
|
||||||
let (processed, verified_txs_len, unprocessed_indexes) = Self::process_received_packets(
|
let (processed, verified_txs_len, unprocessed_indexes) =
|
||||||
|
Self::process_packets_transactions(
|
||||||
&bank,
|
&bank,
|
||||||
|
&bank_creation_time,
|
||||||
&poh,
|
&poh,
|
||||||
&msgs,
|
&msgs,
|
||||||
packet_indexes,
|
packet_indexes,
|
||||||
|
@ -1165,6 +1193,7 @@ impl BankingStage {
|
||||||
duplicates,
|
duplicates,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If there were retryable transactions, add the unexpired ones to the buffered queue
|
||||||
if processed < verified_txs_len {
|
if processed < verified_txs_len {
|
||||||
let next_leader = poh.lock().unwrap().next_slot_leader();
|
let next_leader = poh.lock().unwrap().next_slot_leader();
|
||||||
// Walk thru rest of the transactions and filter out the invalid (e.g. too old) ones
|
// Walk thru rest of the transactions and filter out the invalid (e.g. too old) ones
|
||||||
|
@ -1661,8 +1690,10 @@ mod tests {
|
||||||
..
|
..
|
||||||
} = create_genesis_config(10_000);
|
} = create_genesis_config(10_000);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: bank.tick_height(),
|
min_tick_height: bank.tick_height(),
|
||||||
max_tick_height: std::u64::MAX,
|
max_tick_height: std::u64::MAX,
|
||||||
};
|
};
|
||||||
|
@ -1915,80 +1946,80 @@ mod tests {
|
||||||
fn test_should_process_or_forward_packets() {
|
fn test_should_process_or_forward_packets() {
|
||||||
let my_pubkey = solana_sdk::pubkey::new_rand();
|
let my_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let my_pubkey1 = solana_sdk::pubkey::new_rand();
|
let my_pubkey1 = solana_sdk::pubkey::new_rand();
|
||||||
|
let bank = Arc::new(Bank::default());
|
||||||
assert_eq!(
|
assert_matches!(
|
||||||
BankingStage::consume_or_forward_packets(&my_pubkey, None, true, false, false),
|
BankingStage::consume_or_forward_packets(&my_pubkey, None, Some(&bank), false, false),
|
||||||
BufferedPacketsDecision::Hold
|
BufferedPacketsDecision::Hold
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_matches!(
|
||||||
BankingStage::consume_or_forward_packets(&my_pubkey, None, false, false, false),
|
BankingStage::consume_or_forward_packets(&my_pubkey, None, None, false, false),
|
||||||
BufferedPacketsDecision::Hold
|
BufferedPacketsDecision::Hold
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_matches!(
|
||||||
BankingStage::consume_or_forward_packets(&my_pubkey1, None, false, false, false),
|
BankingStage::consume_or_forward_packets(&my_pubkey1, None, None, false, false),
|
||||||
BufferedPacketsDecision::Hold
|
BufferedPacketsDecision::Hold
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_matches!(
|
||||||
BankingStage::consume_or_forward_packets(
|
BankingStage::consume_or_forward_packets(
|
||||||
&my_pubkey,
|
&my_pubkey,
|
||||||
Some(my_pubkey1),
|
Some(my_pubkey1),
|
||||||
false,
|
None,
|
||||||
false,
|
false,
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
BufferedPacketsDecision::Forward
|
BufferedPacketsDecision::Forward
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_matches!(
|
||||||
BankingStage::consume_or_forward_packets(
|
BankingStage::consume_or_forward_packets(
|
||||||
&my_pubkey,
|
&my_pubkey,
|
||||||
Some(my_pubkey1),
|
Some(my_pubkey1),
|
||||||
false,
|
None,
|
||||||
true,
|
true,
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
BufferedPacketsDecision::Hold
|
BufferedPacketsDecision::Hold
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_matches!(
|
||||||
BankingStage::consume_or_forward_packets(
|
BankingStage::consume_or_forward_packets(
|
||||||
&my_pubkey,
|
&my_pubkey,
|
||||||
Some(my_pubkey1),
|
Some(my_pubkey1),
|
||||||
false,
|
None,
|
||||||
true,
|
true,
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
BufferedPacketsDecision::ForwardAndHold
|
BufferedPacketsDecision::ForwardAndHold
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_matches!(
|
||||||
BankingStage::consume_or_forward_packets(
|
BankingStage::consume_or_forward_packets(
|
||||||
&my_pubkey,
|
&my_pubkey,
|
||||||
Some(my_pubkey1),
|
Some(my_pubkey1),
|
||||||
true,
|
Some(&bank),
|
||||||
false,
|
false,
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
BufferedPacketsDecision::Consume
|
BufferedPacketsDecision::Consume(_)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_matches!(
|
||||||
BankingStage::consume_or_forward_packets(
|
BankingStage::consume_or_forward_packets(
|
||||||
&my_pubkey1,
|
&my_pubkey1,
|
||||||
Some(my_pubkey1),
|
Some(my_pubkey1),
|
||||||
false,
|
None,
|
||||||
false,
|
false,
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
BufferedPacketsDecision::Hold
|
BufferedPacketsDecision::Hold
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_matches!(
|
||||||
BankingStage::consume_or_forward_packets(
|
BankingStage::consume_or_forward_packets(
|
||||||
&my_pubkey1,
|
&my_pubkey1,
|
||||||
Some(my_pubkey1),
|
Some(my_pubkey1),
|
||||||
true,
|
Some(&bank),
|
||||||
false,
|
false,
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
BufferedPacketsDecision::Consume
|
BufferedPacketsDecision::Consume(_)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2010,8 +2041,10 @@ mod tests {
|
||||||
genesis_config.hash(),
|
genesis_config.hash(),
|
||||||
)];
|
)];
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: bank.tick_height(),
|
min_tick_height: bank.tick_height(),
|
||||||
max_tick_height: bank.tick_height() + 1,
|
max_tick_height: bank.tick_height() + 1,
|
||||||
};
|
};
|
||||||
|
@ -2106,8 +2139,10 @@ mod tests {
|
||||||
system_transaction::transfer(&mint_keypair, &pubkey1, 1, genesis_config.hash()),
|
system_transaction::transfer(&mint_keypair, &pubkey1, 1, genesis_config.hash()),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: bank.tick_height(),
|
min_tick_height: bank.tick_height(),
|
||||||
max_tick_height: bank.tick_height() + 1,
|
max_tick_height: bank.tick_height() + 1,
|
||||||
};
|
};
|
||||||
|
@ -2196,6 +2231,9 @@ mod tests {
|
||||||
mint_keypair,
|
mint_keypair,
|
||||||
..
|
..
|
||||||
} = create_genesis_config(10_000);
|
} = create_genesis_config(10_000);
|
||||||
|
let mut bank = Bank::new(&genesis_config);
|
||||||
|
// Allow arbitrary transaction processing time for the purposes of this test
|
||||||
|
bank.ns_per_slot = std::u128::MAX;
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
|
|
||||||
let pubkey = solana_sdk::pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
|
@ -2231,6 +2269,7 @@ mod tests {
|
||||||
let (processed_transactions_count, mut retryable_txs) =
|
let (processed_transactions_count, mut retryable_txs) =
|
||||||
BankingStage::process_transactions(
|
BankingStage::process_transactions(
|
||||||
&bank,
|
&bank,
|
||||||
|
&Instant::now(),
|
||||||
&transactions,
|
&transactions,
|
||||||
&poh_recorder,
|
&poh_recorder,
|
||||||
None,
|
None,
|
||||||
|
@ -2276,8 +2315,10 @@ mod tests {
|
||||||
let transactions = vec![success_tx, ix_error_tx, fail_tx];
|
let transactions = vec![success_tx, ix_error_tx, fail_tx];
|
||||||
bank.transfer(4, &mint_keypair, &keypair1.pubkey()).unwrap();
|
bank.transfer(4, &mint_keypair, &keypair1.pubkey()).unwrap();
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: bank.tick_height(),
|
min_tick_height: bank.tick_height(),
|
||||||
max_tick_height: bank.tick_height() + 1,
|
max_tick_height: bank.tick_height() + 1,
|
||||||
};
|
};
|
||||||
|
@ -2420,8 +2461,10 @@ mod tests {
|
||||||
|
|
||||||
// When the working bank in poh_recorder is None, no packets should be processed
|
// When the working bank in poh_recorder is None, no packets should be processed
|
||||||
assert!(!poh_recorder.lock().unwrap().has_bank());
|
assert!(!poh_recorder.lock().unwrap().has_bank());
|
||||||
|
let max_tx_processing_ns = std::u128::MAX;
|
||||||
BankingStage::consume_buffered_packets(
|
BankingStage::consume_buffered_packets(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
|
max_tx_processing_ns,
|
||||||
&poh_recorder,
|
&poh_recorder,
|
||||||
&mut buffered_packets,
|
&mut buffered_packets,
|
||||||
None,
|
None,
|
||||||
|
@ -2436,6 +2479,7 @@ mod tests {
|
||||||
poh_recorder.lock().unwrap().set_bank(&bank);
|
poh_recorder.lock().unwrap().set_bank(&bank);
|
||||||
BankingStage::consume_buffered_packets(
|
BankingStage::consume_buffered_packets(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
|
max_tx_processing_ns,
|
||||||
&poh_recorder,
|
&poh_recorder,
|
||||||
&mut buffered_packets,
|
&mut buffered_packets,
|
||||||
None,
|
None,
|
||||||
|
@ -2492,6 +2536,7 @@ mod tests {
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
BankingStage::consume_buffered_packets(
|
BankingStage::consume_buffered_packets(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
|
std::u128::MAX,
|
||||||
&poh_recorder_,
|
&poh_recorder_,
|
||||||
&mut buffered_packets,
|
&mut buffered_packets,
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -49,10 +49,12 @@ pub enum PohRecorderError {
|
||||||
type Result<T> = std::result::Result<T, PohRecorderError>;
|
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>);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WorkingBank {
|
pub struct WorkingBank {
|
||||||
pub bank: Arc<Bank>,
|
pub bank: Arc<Bank>,
|
||||||
|
pub start: Arc<Instant>,
|
||||||
pub min_tick_height: u64,
|
pub min_tick_height: u64,
|
||||||
pub max_tick_height: u64,
|
pub max_tick_height: u64,
|
||||||
}
|
}
|
||||||
|
@ -97,7 +99,14 @@ impl PohRecorder {
|
||||||
self.grace_ticks = grace_ticks;
|
self.grace_ticks = grace_ticks;
|
||||||
self.leader_first_tick_height = leader_first_tick_height;
|
self.leader_first_tick_height = leader_first_tick_height;
|
||||||
self.leader_last_tick_height = leader_last_tick_height;
|
self.leader_last_tick_height = leader_last_tick_height;
|
||||||
|
|
||||||
|
datapoint_info!(
|
||||||
|
"leader-slot-start-to-cleared-elapsed-ms",
|
||||||
|
("slot", bank.slot(), i64),
|
||||||
|
("elapsed", working_bank.start.elapsed().as_millis(), i64),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref signal) = self.clear_bank_signal {
|
if let Some(ref signal) = self.clear_bank_signal {
|
||||||
let _ = signal.try_send(true);
|
let _ = signal.try_send(true);
|
||||||
}
|
}
|
||||||
|
@ -126,7 +135,13 @@ impl PohRecorder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bank(&self) -> Option<Arc<Bank>> {
|
pub fn bank(&self) -> Option<Arc<Bank>> {
|
||||||
self.working_bank.clone().map(|w| w.bank)
|
self.working_bank.as_ref().map(|w| w.bank.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bank_start(&self) -> Option<BankStart> {
|
||||||
|
self.working_bank
|
||||||
|
.as_ref()
|
||||||
|
.map(|w| (w.bank.clone(), w.start.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_bank(&self) -> bool {
|
pub fn has_bank(&self) -> bool {
|
||||||
|
@ -273,11 +288,15 @@ impl PohRecorder {
|
||||||
trace!("new working bank");
|
trace!("new working bank");
|
||||||
assert_eq!(working_bank.bank.ticks_per_slot(), self.ticks_per_slot());
|
assert_eq!(working_bank.bank.ticks_per_slot(), self.ticks_per_slot());
|
||||||
self.working_bank = Some(working_bank);
|
self.working_bank = Some(working_bank);
|
||||||
|
// TODO: adjust the working_bank.start time based on number of ticks
|
||||||
|
// that have already elapsed based on current tick height.
|
||||||
let _ = self.flush_cache(false);
|
let _ = self.flush_cache(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_bank(&mut self, bank: &Arc<Bank>) {
|
pub fn set_bank(&mut self, bank: &Arc<Bank>) {
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start: Arc::new(Instant::now()),
|
||||||
min_tick_height: bank.tick_height(),
|
min_tick_height: bank.tick_height(),
|
||||||
max_tick_height: bank.max_tick_height(),
|
max_tick_height: bank.max_tick_height(),
|
||||||
};
|
};
|
||||||
|
@ -517,6 +536,18 @@ impl PohRecorder {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filters the return result of PohRecorder::bank_start(), returns the bank
|
||||||
|
// if it's still processing transactions
|
||||||
|
pub fn get_bank_still_processing_txs(bank_start: &Option<BankStart>) -> Option<&Arc<Bank>> {
|
||||||
|
bank_start.as_ref().and_then(|(bank, bank_creation_time)| {
|
||||||
|
if Bank::should_bank_still_be_processing_txs(bank_creation_time, bank.ns_per_slot) {
|
||||||
|
Some(bank)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn schedule_dummy_max_height_reached_failure(&mut self) {
|
pub fn schedule_dummy_max_height_reached_failure(&mut self) {
|
||||||
self.reset(Hash::default(), 1, None);
|
self.reset(Hash::default(), 1, None);
|
||||||
|
@ -635,8 +666,10 @@ mod tests {
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank,
|
bank,
|
||||||
|
start,
|
||||||
min_tick_height: 2,
|
min_tick_height: 2,
|
||||||
max_tick_height: 3,
|
max_tick_height: 3,
|
||||||
};
|
};
|
||||||
|
@ -669,8 +702,10 @@ mod tests {
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: 2,
|
min_tick_height: 2,
|
||||||
max_tick_height: 3,
|
max_tick_height: 3,
|
||||||
};
|
};
|
||||||
|
@ -725,8 +760,10 @@ mod tests {
|
||||||
assert_eq!(poh_recorder.tick_cache.last().unwrap().1, 4);
|
assert_eq!(poh_recorder.tick_cache.last().unwrap().1, 4);
|
||||||
assert_eq!(poh_recorder.tick_height, 4);
|
assert_eq!(poh_recorder.tick_height, 4);
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank,
|
bank,
|
||||||
|
start,
|
||||||
min_tick_height: 2,
|
min_tick_height: 2,
|
||||||
max_tick_height: 3,
|
max_tick_height: 3,
|
||||||
};
|
};
|
||||||
|
@ -765,8 +802,10 @@ mod tests {
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: 2,
|
min_tick_height: 2,
|
||||||
max_tick_height: 3,
|
max_tick_height: 3,
|
||||||
};
|
};
|
||||||
|
@ -801,8 +840,10 @@ mod tests {
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: 1,
|
min_tick_height: 1,
|
||||||
max_tick_height: 2,
|
max_tick_height: 2,
|
||||||
};
|
};
|
||||||
|
@ -841,8 +882,10 @@ mod tests {
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: 1,
|
min_tick_height: 1,
|
||||||
max_tick_height: 2,
|
max_tick_height: 2,
|
||||||
};
|
};
|
||||||
|
@ -885,8 +928,10 @@ mod tests {
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: 1,
|
min_tick_height: 1,
|
||||||
max_tick_height: 2,
|
max_tick_height: 2,
|
||||||
};
|
};
|
||||||
|
@ -927,8 +972,10 @@ mod tests {
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank,
|
bank,
|
||||||
|
start,
|
||||||
min_tick_height: 2,
|
min_tick_height: 2,
|
||||||
max_tick_height: 3,
|
max_tick_height: 3,
|
||||||
};
|
};
|
||||||
|
@ -1049,8 +1096,10 @@ 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 start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank,
|
bank,
|
||||||
|
start,
|
||||||
min_tick_height: 2,
|
min_tick_height: 2,
|
||||||
max_tick_height: 3,
|
max_tick_height: 3,
|
||||||
};
|
};
|
||||||
|
@ -1118,8 +1167,10 @@ mod tests {
|
||||||
|
|
||||||
let end_slot = 3;
|
let end_slot = 3;
|
||||||
let max_tick_height = (end_slot + 1) * ticks_per_slot;
|
let max_tick_height = (end_slot + 1) * ticks_per_slot;
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: 1,
|
min_tick_height: 1,
|
||||||
max_tick_height,
|
max_tick_height,
|
||||||
};
|
};
|
||||||
|
|
|
@ -208,8 +208,10 @@ mod tests {
|
||||||
);
|
);
|
||||||
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
||||||
let exit = Arc::new(AtomicBool::new(false));
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
|
let start = Arc::new(Instant::now());
|
||||||
let working_bank = WorkingBank {
|
let working_bank = WorkingBank {
|
||||||
bank: bank.clone(),
|
bank: bank.clone(),
|
||||||
|
start,
|
||||||
min_tick_height: bank.tick_height(),
|
min_tick_height: bank.tick_height(),
|
||||||
max_tick_height: std::u64::MAX,
|
max_tick_height: std::u64::MAX,
|
||||||
};
|
};
|
||||||
|
|
|
@ -90,6 +90,7 @@ use std::{
|
||||||
LockResult, RwLockWriteGuard, {Arc, RwLock, RwLockReadGuard},
|
LockResult, RwLockWriteGuard, {Arc, RwLock, RwLockReadGuard},
|
||||||
},
|
},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SECONDS_PER_YEAR: f64 = 365.25 * 24.0 * 60.0 * 60.0;
|
pub const SECONDS_PER_YEAR: f64 = 365.25 * 24.0 * 60.0 * 60.0;
|
||||||
|
@ -782,7 +783,7 @@ pub struct Bank {
|
||||||
ticks_per_slot: u64,
|
ticks_per_slot: u64,
|
||||||
|
|
||||||
/// length of a slot in ns
|
/// length of a slot in ns
|
||||||
ns_per_slot: u128,
|
pub ns_per_slot: u128,
|
||||||
|
|
||||||
/// genesis time, used for computed clock
|
/// genesis time, used for computed clock
|
||||||
genesis_creation_time: UnixTimestamp,
|
genesis_creation_time: UnixTimestamp,
|
||||||
|
@ -4638,6 +4639,16 @@ impl Bank {
|
||||||
.is_active(&feature_set::check_init_vote_data::id())
|
.is_active(&feature_set::check_init_vote_data::id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the wallclock time from bank creation to now has exceeded the allotted
|
||||||
|
// time for transaction processing
|
||||||
|
pub fn should_bank_still_be_processing_txs(
|
||||||
|
bank_creation_time: &Instant,
|
||||||
|
max_tx_ingestion_nanos: u128,
|
||||||
|
) -> bool {
|
||||||
|
// Do this check outside of the poh lock, hence not a method on PohRecorder
|
||||||
|
bank_creation_time.elapsed().as_nanos() <= max_tx_ingestion_nanos
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deactivate_feature(&mut self, id: &Pubkey) {
|
pub fn deactivate_feature(&mut self, id: &Pubkey) {
|
||||||
let mut feature_set = Arc::make_mut(&mut self.feature_set).clone();
|
let mut feature_set = Arc::make_mut(&mut self.feature_set).clone();
|
||||||
feature_set.active.remove(&id);
|
feature_set.active.remove(&id);
|
||||||
|
|
|
@ -6,6 +6,8 @@ pub const DEFAULT_TICKS_PER_SECOND: u64 = 160;
|
||||||
|
|
||||||
pub const MS_PER_TICK: u64 = 1000 / DEFAULT_TICKS_PER_SECOND;
|
pub const MS_PER_TICK: u64 = 1000 / DEFAULT_TICKS_PER_SECOND;
|
||||||
|
|
||||||
|
pub const SLOT_MS: u64 = (DEFAULT_TICKS_PER_SLOT * 1000) / DEFAULT_TICKS_PER_SECOND;
|
||||||
|
|
||||||
// At 160 ticks/s, 64 ticks per slot implies that leader rotation and voting will happen
|
// At 160 ticks/s, 64 ticks per slot implies that leader rotation and voting will happen
|
||||||
// every 400 ms. A fast voting cadence ensures faster finality and convergence
|
// every 400 ms. A fast voting cadence ensures faster finality and convergence
|
||||||
pub const DEFAULT_TICKS_PER_SLOT: u64 = 64;
|
pub const DEFAULT_TICKS_PER_SLOT: u64 = 64;
|
||||||
|
|
Loading…
Reference in New Issue