Add option to reclaim accounts-cluster-bench accounts/lamports (#21656)
* Add option to reclaim accounts-cluster-bench accounts/lamports * lint
This commit is contained in:
		
							
								
								
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -4276,6 +4276,7 @@ dependencies = [ | |||||||
|  "solana-runtime", |  "solana-runtime", | ||||||
|  "solana-sdk", |  "solana-sdk", | ||||||
|  "solana-streamer", |  "solana-streamer", | ||||||
|  |  "solana-test-validator", | ||||||
|  "solana-transaction-status", |  "solana-transaction-status", | ||||||
|  "solana-version", |  "solana-version", | ||||||
|  "spl-token", |  "spl-token", | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ solana-net-utils = { path = "../net-utils", version = "=1.10.0" } | |||||||
| solana-runtime = { path = "../runtime", version = "=1.10.0" } | solana-runtime = { path = "../runtime", version = "=1.10.0" } | ||||||
| solana-sdk = { path = "../sdk", version = "=1.10.0" } | solana-sdk = { path = "../sdk", version = "=1.10.0" } | ||||||
| solana-streamer = { path = "../streamer", version = "=1.10.0" } | solana-streamer = { path = "../streamer", version = "=1.10.0" } | ||||||
|  | solana-test-validator = { path = "../test-validator", version = "=1.10.0" } | ||||||
| solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" } | solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" } | ||||||
| solana-version = { path = "../version", version = "=1.10.0" } | solana-version = { path = "../version", version = "=1.10.0" } | ||||||
| spl-token = { version = "=3.2.0", features = ["no-entrypoint"] } | spl-token = { version = "=3.2.0", features = ["no-entrypoint"] } | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ use { | |||||||
|     solana_streamer::socket::SocketAddrSpace, |     solana_streamer::socket::SocketAddrSpace, | ||||||
|     solana_transaction_status::parse_token::spl_token_instruction, |     solana_transaction_status::parse_token::spl_token_instruction, | ||||||
|     std::{ |     std::{ | ||||||
|  |         cmp::min, | ||||||
|         net::SocketAddr, |         net::SocketAddr, | ||||||
|         process::exit, |         process::exit, | ||||||
|         sync::{ |         sync::{ | ||||||
| @@ -156,24 +157,30 @@ fn make_create_message( | |||||||
| fn make_close_message( | fn make_close_message( | ||||||
|     keypair: &Keypair, |     keypair: &Keypair, | ||||||
|     base_keypair: &Keypair, |     base_keypair: &Keypair, | ||||||
|     max_closed_seed: Arc<AtomicU64>, |     max_created: Arc<AtomicU64>, | ||||||
|  |     max_closed: Arc<AtomicU64>, | ||||||
|     num_instructions: usize, |     num_instructions: usize, | ||||||
|     balance: u64, |     balance: u64, | ||||||
|     spl_token: bool, |     spl_token: bool, | ||||||
| ) -> Message { | ) -> Message { | ||||||
|     let instructions: Vec<_> = (0..num_instructions) |     let instructions: Vec<_> = (0..num_instructions) | ||||||
|         .into_iter() |         .into_iter() | ||||||
|         .map(|_| { |         .filter_map(|_| { | ||||||
|             let program_id = if spl_token { |             let program_id = if spl_token { | ||||||
|                 inline_spl_token::id() |                 inline_spl_token::id() | ||||||
|             } else { |             } else { | ||||||
|                 system_program::id() |                 system_program::id() | ||||||
|             }; |             }; | ||||||
|             let seed = max_closed_seed.fetch_add(1, Ordering::Relaxed).to_string(); |             let max_created_seed = max_created.load(Ordering::Relaxed); | ||||||
|  |             let max_closed_seed = max_closed.load(Ordering::Relaxed); | ||||||
|  |             if max_closed_seed >= max_created_seed { | ||||||
|  |                 return None; | ||||||
|  |             } | ||||||
|  |             let seed = max_closed.fetch_add(1, Ordering::Relaxed).to_string(); | ||||||
|             let address = |             let address = | ||||||
|                 Pubkey::create_with_seed(&base_keypair.pubkey(), &seed, &program_id).unwrap(); |                 Pubkey::create_with_seed(&base_keypair.pubkey(), &seed, &program_id).unwrap(); | ||||||
|             if spl_token { |             if spl_token { | ||||||
|                 spl_token_instruction( |                 Some(spl_token_instruction( | ||||||
|                     spl_token::instruction::close_account( |                     spl_token::instruction::close_account( | ||||||
|                         &spl_token::id(), |                         &spl_token::id(), | ||||||
|                         &spl_token_pubkey(&address), |                         &spl_token_pubkey(&address), | ||||||
| @@ -182,16 +189,16 @@ fn make_close_message( | |||||||
|                         &[], |                         &[], | ||||||
|                     ) |                     ) | ||||||
|                     .unwrap(), |                     .unwrap(), | ||||||
|                 ) |                 )) | ||||||
|             } else { |             } else { | ||||||
|                 system_instruction::transfer_with_seed( |                 Some(system_instruction::transfer_with_seed( | ||||||
|                     &address, |                     &address, | ||||||
|                     &base_keypair.pubkey(), |                     &base_keypair.pubkey(), | ||||||
|                     seed, |                     seed, | ||||||
|                     &program_id, |                     &program_id, | ||||||
|                     &keypair.pubkey(), |                     &keypair.pubkey(), | ||||||
|                     balance, |                     balance, | ||||||
|                 ) |                 )) | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
|         .collect(); |         .collect(); | ||||||
| @@ -211,6 +218,7 @@ fn run_accounts_bench( | |||||||
|     maybe_lamports: Option<u64>, |     maybe_lamports: Option<u64>, | ||||||
|     num_instructions: usize, |     num_instructions: usize, | ||||||
|     mint: Option<Pubkey>, |     mint: Option<Pubkey>, | ||||||
|  |     reclaim_accounts: bool, | ||||||
| ) { | ) { | ||||||
|     assert!(num_instructions > 0); |     assert!(num_instructions > 0); | ||||||
|     let client = |     let client = | ||||||
| @@ -350,6 +358,7 @@ fn run_accounts_bench( | |||||||
|                             let message = make_close_message( |                             let message = make_close_message( | ||||||
|                                 payer_keypairs[0], |                                 payer_keypairs[0], | ||||||
|                                 &base_keypair, |                                 &base_keypair, | ||||||
|  |                                 seed_tracker.max_created.clone(), | ||||||
|                                 seed_tracker.max_closed.clone(), |                                 seed_tracker.max_closed.clone(), | ||||||
|                                 1, |                                 1, | ||||||
|                                 min_balance, |                                 min_balance, | ||||||
| @@ -372,7 +381,7 @@ fn run_accounts_bench( | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         count += 1; |         count += 1; | ||||||
|         if last_log.elapsed().as_millis() > 3000 { |         if last_log.elapsed().as_millis() > 3000 || count >= iterations { | ||||||
|             info!( |             info!( | ||||||
|                 "total_accounts_created: {} total_accounts_closed: {} tx_sent_count: {} loop_count: {} balance(s): {:?}", |                 "total_accounts_created: {} total_accounts_closed: {} tx_sent_count: {} loop_count: {} balance(s): {:?}", | ||||||
|                 total_accounts_created, total_accounts_closed, tx_sent_count, count, balances |                 total_accounts_created, total_accounts_closed, tx_sent_count, count, balances | ||||||
| @@ -387,6 +396,83 @@ fn run_accounts_bench( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     executor.close(); |     executor.close(); | ||||||
|  |  | ||||||
|  |     if reclaim_accounts { | ||||||
|  |         let executor = TransactionExecutor::new(entrypoint_addr); | ||||||
|  |         loop { | ||||||
|  |             let max_closed_seed = seed_tracker.max_closed.load(Ordering::Relaxed); | ||||||
|  |             let max_created_seed = seed_tracker.max_created.load(Ordering::Relaxed); | ||||||
|  |  | ||||||
|  |             if latest_blockhash.elapsed().as_millis() > 10_000 { | ||||||
|  |                 blockhash = client.get_latest_blockhash().expect("blockhash"); | ||||||
|  |                 latest_blockhash = Instant::now(); | ||||||
|  |             } | ||||||
|  |             message.recent_blockhash = blockhash; | ||||||
|  |             let fee = client | ||||||
|  |                 .get_fee_for_message(&message) | ||||||
|  |                 .expect("get_fee_for_message"); | ||||||
|  |  | ||||||
|  |             let sigs_len = executor.num_outstanding(); | ||||||
|  |             if sigs_len < batch_size && max_closed_seed < max_created_seed { | ||||||
|  |                 let num_to_close = min( | ||||||
|  |                     batch_size - sigs_len, | ||||||
|  |                     (max_created_seed - max_closed_seed) as usize, | ||||||
|  |                 ); | ||||||
|  |                 if num_to_close >= payer_keypairs.len() { | ||||||
|  |                     info!("closing {} accounts", num_to_close); | ||||||
|  |                     let chunk_size = num_to_close / payer_keypairs.len(); | ||||||
|  |                     info!("{:?} chunk_size", chunk_size); | ||||||
|  |                     if chunk_size > 0 { | ||||||
|  |                         for (i, keypair) in payer_keypairs.iter().enumerate() { | ||||||
|  |                             let txs: Vec<_> = (0..chunk_size) | ||||||
|  |                                 .into_par_iter() | ||||||
|  |                                 .filter_map(|_| { | ||||||
|  |                                     let message = make_close_message( | ||||||
|  |                                         keypair, | ||||||
|  |                                         &base_keypair, | ||||||
|  |                                         seed_tracker.max_created.clone(), | ||||||
|  |                                         seed_tracker.max_closed.clone(), | ||||||
|  |                                         num_instructions, | ||||||
|  |                                         min_balance, | ||||||
|  |                                         mint.is_some(), | ||||||
|  |                                     ); | ||||||
|  |                                     if message.instructions.is_empty() { | ||||||
|  |                                         return None; | ||||||
|  |                                     } | ||||||
|  |                                     let signers: Vec<&Keypair> = vec![keypair, &base_keypair]; | ||||||
|  |                                     Some(Transaction::new(&signers, message, blockhash)) | ||||||
|  |                                 }) | ||||||
|  |                                 .collect(); | ||||||
|  |                             balances[i] = balances[i].saturating_sub(fee * txs.len() as u64); | ||||||
|  |                             info!("close txs: {}", txs.len()); | ||||||
|  |                             let new_ids = executor.push_transactions(txs); | ||||||
|  |                             info!("close ids: {}", new_ids.len()); | ||||||
|  |                             tx_sent_count += new_ids.len(); | ||||||
|  |                             total_accounts_closed += (num_instructions * new_ids.len()) as u64; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 let _ = executor.drain_cleared(); | ||||||
|  |             } | ||||||
|  |             count += 1; | ||||||
|  |             if last_log.elapsed().as_millis() > 3000 || max_closed_seed >= max_created_seed { | ||||||
|  |                 info!( | ||||||
|  |                     "total_accounts_closed: {} tx_sent_count: {} loop_count: {} balance(s): {:?}", | ||||||
|  |                     total_accounts_closed, tx_sent_count, count, balances | ||||||
|  |                 ); | ||||||
|  |                 last_log = Instant::now(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if max_closed_seed >= max_created_seed { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             if executor.num_outstanding() >= batch_size { | ||||||
|  |                 sleep(Duration::from_millis(500)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         executor.close(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn main() { | fn main() { | ||||||
| @@ -462,7 +548,7 @@ fn main() { | |||||||
|                 .long("iterations") |                 .long("iterations") | ||||||
|                 .takes_value(true) |                 .takes_value(true) | ||||||
|                 .value_name("NUM") |                 .value_name("NUM") | ||||||
|                 .help("Number of iterations to make"), |                 .help("Number of iterations to make. 0 = unlimited iterations."), | ||||||
|         ) |         ) | ||||||
|         .arg( |         .arg( | ||||||
|             Arg::with_name("check_gossip") |             Arg::with_name("check_gossip") | ||||||
| @@ -475,6 +561,12 @@ fn main() { | |||||||
|                 .takes_value(true) |                 .takes_value(true) | ||||||
|                 .help("Mint address to initialize account"), |                 .help("Mint address to initialize account"), | ||||||
|         ) |         ) | ||||||
|  |         .arg( | ||||||
|  |             Arg::with_name("reclaim_accounts") | ||||||
|  |                 .long("reclaim-accounts") | ||||||
|  |                 .takes_value(false) | ||||||
|  |                 .help("Reclaim accounts after session ends; incompatible with --iterations 0"), | ||||||
|  |         ) | ||||||
|         .get_matches(); |         .get_matches(); | ||||||
|  |  | ||||||
|     let skip_gossip = !matches.is_present("check_gossip"); |     let skip_gossip = !matches.is_present("check_gossip"); | ||||||
| @@ -556,6 +648,7 @@ fn main() { | |||||||
|         lamports, |         lamports, | ||||||
|         num_instructions, |         num_instructions, | ||||||
|         mint, |         mint, | ||||||
|  |         matches.is_present("reclaim_accounts"), | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -564,12 +657,18 @@ pub mod test { | |||||||
|     use { |     use { | ||||||
|         super::*, |         super::*, | ||||||
|         solana_core::validator::ValidatorConfig, |         solana_core::validator::ValidatorConfig, | ||||||
|  |         solana_faucet::faucet::run_local_faucet, | ||||||
|         solana_local_cluster::{ |         solana_local_cluster::{ | ||||||
|             local_cluster::{ClusterConfig, LocalCluster}, |             local_cluster::{ClusterConfig, LocalCluster}, | ||||||
|             validator_configs::make_identical_validator_configs, |             validator_configs::make_identical_validator_configs, | ||||||
|         }, |         }, | ||||||
|         solana_measure::measure::Measure, |         solana_measure::measure::Measure, | ||||||
|         solana_sdk::poh_config::PohConfig, |         solana_sdk::{native_token::sol_to_lamports, poh_config::PohConfig}, | ||||||
|  |         solana_test_validator::TestValidator, | ||||||
|  |         spl_token::{ | ||||||
|  |             solana_program::program_pack::Pack, | ||||||
|  |             state::{Account, Mint}, | ||||||
|  |         }, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
| @@ -605,6 +704,108 @@ pub mod test { | |||||||
|             maybe_lamports, |             maybe_lamports, | ||||||
|             num_instructions, |             num_instructions, | ||||||
|             None, |             None, | ||||||
|  |             false, | ||||||
|  |         ); | ||||||
|  |         start.stop(); | ||||||
|  |         info!("{}", start); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_create_then_reclaim_spl_token_accounts() { | ||||||
|  |         solana_logger::setup(); | ||||||
|  |         let mint_keypair = Keypair::new(); | ||||||
|  |         let mint_pubkey = mint_keypair.pubkey(); | ||||||
|  |         let faucet_addr = run_local_faucet(mint_keypair, None); | ||||||
|  |         let test_validator = TestValidator::with_custom_fees( | ||||||
|  |             mint_pubkey, | ||||||
|  |             1, | ||||||
|  |             Some(faucet_addr), | ||||||
|  |             SocketAddrSpace::Unspecified, | ||||||
|  |         ); | ||||||
|  |         let rpc_client = | ||||||
|  |             RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed()); | ||||||
|  |  | ||||||
|  |         // Created funder | ||||||
|  |         let funder = Keypair::new(); | ||||||
|  |         let latest_blockhash = rpc_client.get_latest_blockhash().unwrap(); | ||||||
|  |         let signature = rpc_client | ||||||
|  |             .request_airdrop_with_blockhash( | ||||||
|  |                 &funder.pubkey(), | ||||||
|  |                 sol_to_lamports(1.0), | ||||||
|  |                 &latest_blockhash, | ||||||
|  |             ) | ||||||
|  |             .unwrap(); | ||||||
|  |         rpc_client | ||||||
|  |             .confirm_transaction_with_spinner( | ||||||
|  |                 &signature, | ||||||
|  |                 &latest_blockhash, | ||||||
|  |                 CommitmentConfig::confirmed(), | ||||||
|  |             ) | ||||||
|  |             .unwrap(); | ||||||
|  |  | ||||||
|  |         // Create Mint | ||||||
|  |         let spl_mint_keypair = Keypair::new(); | ||||||
|  |         let spl_mint_len = Mint::get_packed_len(); | ||||||
|  |         let spl_mint_rent = rpc_client | ||||||
|  |             .get_minimum_balance_for_rent_exemption(spl_mint_len) | ||||||
|  |             .unwrap(); | ||||||
|  |         let transaction = Transaction::new_signed_with_payer( | ||||||
|  |             &[ | ||||||
|  |                 system_instruction::create_account( | ||||||
|  |                     &funder.pubkey(), | ||||||
|  |                     &spl_mint_keypair.pubkey(), | ||||||
|  |                     spl_mint_rent, | ||||||
|  |                     spl_mint_len as u64, | ||||||
|  |                     &inline_spl_token::id(), | ||||||
|  |                 ), | ||||||
|  |                 spl_token_instruction( | ||||||
|  |                     spl_token::instruction::initialize_mint( | ||||||
|  |                         &spl_token::id(), | ||||||
|  |                         &spl_token_pubkey(&spl_mint_keypair.pubkey()), | ||||||
|  |                         &spl_token_pubkey(&spl_mint_keypair.pubkey()), | ||||||
|  |                         None, | ||||||
|  |                         2, | ||||||
|  |                     ) | ||||||
|  |                     .unwrap(), | ||||||
|  |                 ), | ||||||
|  |             ], | ||||||
|  |             Some(&funder.pubkey()), | ||||||
|  |             &[&funder, &spl_mint_keypair], | ||||||
|  |             latest_blockhash, | ||||||
|  |         ); | ||||||
|  |         let _sig = rpc_client | ||||||
|  |             .send_and_confirm_transaction(&transaction) | ||||||
|  |             .unwrap(); | ||||||
|  |  | ||||||
|  |         let account_len = Account::get_packed_len(); | ||||||
|  |         let minimum_balance = rpc_client | ||||||
|  |             .get_minimum_balance_for_rent_exemption(account_len) | ||||||
|  |             .unwrap(); | ||||||
|  |  | ||||||
|  |         let iterations = 5; | ||||||
|  |         let batch_size = 100; | ||||||
|  |         let close_nth_batch = 0; | ||||||
|  |         let num_instructions = 4; | ||||||
|  |         let mut start = Measure::start("total accounts run"); | ||||||
|  |         let keypair0 = Keypair::new(); | ||||||
|  |         let keypair1 = Keypair::new(); | ||||||
|  |         let keypair2 = Keypair::new(); | ||||||
|  |         run_accounts_bench( | ||||||
|  |             test_validator | ||||||
|  |                 .rpc_url() | ||||||
|  |                 .replace("http://", "") | ||||||
|  |                 .parse() | ||||||
|  |                 .unwrap(), | ||||||
|  |             faucet_addr, | ||||||
|  |             &[&keypair0, &keypair1, &keypair2], | ||||||
|  |             iterations, | ||||||
|  |             Some(account_len as u64), | ||||||
|  |             batch_size, | ||||||
|  |             close_nth_batch, | ||||||
|  |             Some(minimum_balance), | ||||||
|  |             num_instructions, | ||||||
|  |             Some(spl_mint_keypair.pubkey()), | ||||||
|  |             true, | ||||||
|         ); |         ); | ||||||
|         start.stop(); |         start.stop(); | ||||||
|         info!("{}", start); |         info!("{}", start); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user