Add node health check to transaction preflight

This commit is contained in:
Michael Vines
2020-05-30 00:39:24 -07:00
parent 9dbf3d5430
commit 9158479dc4
3 changed files with 45 additions and 17 deletions

View File

@ -6,6 +6,7 @@ use crate::{
contact_info::ContactInfo, contact_info::ContactInfo,
non_circulating_supply::calculate_non_circulating_supply, non_circulating_supply::calculate_non_circulating_supply,
rpc_error::RpcCustomError, rpc_error::RpcCustomError,
rpc_health::*,
validator::ValidatorExit, validator::ValidatorExit,
}; };
use bincode::serialize; use bincode::serialize;
@ -74,6 +75,7 @@ pub struct JsonRpcRequestProcessor {
blockstore: Arc<Blockstore>, blockstore: Arc<Blockstore>,
config: JsonRpcConfig, config: JsonRpcConfig,
validator_exit: Arc<RwLock<Option<ValidatorExit>>>, validator_exit: Arc<RwLock<Option<ValidatorExit>>>,
health: Arc<RpcHealth>,
} }
impl JsonRpcRequestProcessor { impl JsonRpcRequestProcessor {
@ -128,6 +130,7 @@ impl JsonRpcRequestProcessor {
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>, block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
blockstore: Arc<Blockstore>, blockstore: Arc<Blockstore>,
validator_exit: Arc<RwLock<Option<ValidatorExit>>>, validator_exit: Arc<RwLock<Option<ValidatorExit>>>,
health: Arc<RpcHealth>,
) -> Self { ) -> Self {
JsonRpcRequestProcessor { JsonRpcRequestProcessor {
config, config,
@ -135,6 +138,7 @@ impl JsonRpcRequestProcessor {
block_commitment_cache, block_commitment_cache,
blockstore, blockstore,
validator_exit, validator_exit,
health,
} }
} }
@ -1442,6 +1446,13 @@ impl RpcSol for RpcSolImpl {
.into()); .into());
} }
if meta.request_processor.read().unwrap().health.check() != RpcHealthStatus::Ok {
return Err(RpcCustomError::SendTransactionPreflightFailure {
message: "RPC node is unhealthy, unable to simulate transaction".into(),
}
.into());
}
let bank = &*meta.request_processor.read().unwrap().bank(None)?; let bank = &*meta.request_processor.read().unwrap().bank(None)?;
if let Err(err) = run_transaction_simulation(&bank, &[transaction]) { if let Err(err) = run_transaction_simulation(&bank, &[transaction]) {
// Note: it's possible that the transaction simulation failed but the actual // Note: it's possible that the transaction simulation failed but the actual
@ -1832,6 +1843,7 @@ pub mod tests {
block_commitment_cache.clone(), block_commitment_cache.clone(),
blockstore, blockstore,
validator_exit, validator_exit,
RpcHealth::stub(),
))); )));
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default())); let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default()));
@ -1880,6 +1892,7 @@ pub mod tests {
block_commitment_cache, block_commitment_cache,
blockstore, blockstore,
validator_exit, validator_exit,
RpcHealth::stub(),
); );
thread::spawn(move || { thread::spawn(move || {
let blockhash = bank.confirmed_last_blockhash().0; let blockhash = bank.confirmed_last_blockhash().0;
@ -2824,6 +2837,7 @@ pub mod tests {
block_commitment_cache, block_commitment_cache,
blockstore, blockstore,
validator_exit, validator_exit,
RpcHealth::stub(),
); );
Arc::new(RwLock::new(request_processor)) Arc::new(RwLock::new(request_processor))
}, },
@ -2894,7 +2908,9 @@ pub mod tests {
) )
} }
pub fn create_validator_exit(exit: &Arc<AtomicBool>) -> Arc<RwLock<Option<ValidatorExit>>> { pub(crate) fn create_validator_exit(
exit: &Arc<AtomicBool>,
) -> Arc<RwLock<Option<ValidatorExit>>> {
let mut validator_exit = ValidatorExit::default(); let mut validator_exit = ValidatorExit::default();
let exit_ = exit.clone(); let exit_ = exit.clone();
validator_exit.register_exit(Box::new(move || exit_.store(true, Ordering::Relaxed))); validator_exit.register_exit(Box::new(move || exit_.store(true, Ordering::Relaxed)));
@ -2916,6 +2932,7 @@ pub mod tests {
block_commitment_cache, block_commitment_cache,
blockstore, blockstore,
validator_exit, validator_exit,
RpcHealth::stub(),
); );
assert_eq!(request_processor.validator_exit(), Ok(false)); assert_eq!(request_processor.validator_exit(), Ok(false));
assert_eq!(exit.load(Ordering::Relaxed), false); assert_eq!(exit.load(Ordering::Relaxed), false);
@ -2938,6 +2955,7 @@ pub mod tests {
block_commitment_cache, block_commitment_cache,
blockstore, blockstore,
validator_exit, validator_exit,
RpcHealth::stub(),
); );
assert_eq!(request_processor.validator_exit(), Ok(true)); assert_eq!(request_processor.validator_exit(), Ok(true));
assert_eq!(exit.load(Ordering::Relaxed), true); assert_eq!(exit.load(Ordering::Relaxed), true);
@ -3020,6 +3038,7 @@ pub mod tests {
block_commitment_cache, block_commitment_cache,
blockstore, blockstore,
validator_exit, validator_exit,
RpcHealth::stub(),
); );
assert_eq!( assert_eq!(
request_processor.get_block_commitment(0), request_processor.get_block_commitment(0),

View File

@ -6,6 +6,7 @@ use std::{
sync::Arc, sync::Arc,
}; };
#[derive(PartialEq)]
pub enum RpcHealthStatus { pub enum RpcHealthStatus {
Ok, Ok,
Behind, // Validator is behind its trusted validators Behind, // Validator is behind its trusted validators
@ -88,4 +89,16 @@ impl RpcHealth {
RpcHealthStatus::Ok RpcHealthStatus::Ok
} }
} }
#[cfg(test)]
pub(crate) fn stub() -> Arc<Self> {
Arc::new(Self::new(
Arc::new(ClusterInfo::new_with_invalid_keypair(
crate::contact_info::ContactInfo::default(),
)),
None,
42,
Arc::new(AtomicBool::new(false)),
))
}
} }

View File

@ -255,6 +255,7 @@ impl JsonRpcService {
block_commitment_cache, block_commitment_cache,
blockstore, blockstore,
validator_exit.clone(), validator_exit.clone(),
health.clone(),
))); )));
#[cfg(test)] #[cfg(test)]
@ -431,15 +432,12 @@ mod tests {
#[test] #[test]
fn test_is_file_get_path() { fn test_is_file_get_path() {
let bank_forks = create_bank_forks(); let bank_forks = create_bank_forks();
let health = Arc::new(RpcHealth::new( let rrm = RpcRequestMiddleware::new(
Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default())), PathBuf::from("/"),
None, None,
42, bank_forks.clone(),
Arc::new(AtomicBool::new(false)), RpcHealth::stub(),
)); );
let rrm =
RpcRequestMiddleware::new(PathBuf::from("/"), None, bank_forks.clone(), health.clone());
let rrm_with_snapshot_config = RpcRequestMiddleware::new( let rrm_with_snapshot_config = RpcRequestMiddleware::new(
PathBuf::from("/"), PathBuf::from("/"),
Some(SnapshotConfig { Some(SnapshotConfig {
@ -449,7 +447,7 @@ mod tests {
compression: CompressionType::Bzip2, compression: CompressionType::Bzip2,
}), }),
bank_forks, bank_forks,
health, RpcHealth::stub(),
); );
assert!(rrm.is_file_get_path("/genesis.tar.bz2")); assert!(rrm.is_file_get_path("/genesis.tar.bz2"));
@ -475,14 +473,12 @@ mod tests {
#[test] #[test]
fn test_health_check_with_no_trusted_validators() { fn test_health_check_with_no_trusted_validators() {
let health = Arc::new(RpcHealth::new( let rm = RpcRequestMiddleware::new(
Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default())), PathBuf::from("/"),
None, None,
42, create_bank_forks(),
Arc::new(AtomicBool::new(false)), RpcHealth::stub(),
)); );
let rm = RpcRequestMiddleware::new(PathBuf::from("/"), None, create_bank_forks(), health);
assert_eq!(rm.health_check(), "ok"); assert_eq!(rm.health_check(), "ok");
} }