Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bank snapshots #3202

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 83 additions & 0 deletions core/src/bank_forks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ use std::collections::HashMap;
use std::ops::Index;
use std::sync::Arc;

use solana_runtime::accounts::{deserialize_object, serialize_object, Accounts};
use std::fs;
use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::path::{Path, PathBuf};

pub struct BankForks {
banks: HashMap<u64, Arc<Bank>>,
working_bank: Arc<Bank>,
Expand Down Expand Up @@ -87,6 +93,64 @@ impl BankForks {
pub fn working_bank(&self) -> Arc<Bank> {
self.working_bank.clone()
}

pub fn save_snapshot(&self, path: &str) -> Result<(), ()> {
let mut bank_index: HashMap<u64, PathBuf> = HashMap::new();
let path = Path::new(&path);
fs::create_dir_all(path).unwrap();
for (slot, bank) in &self.frozen_banks() {
let bank_file = format!("bank-{}.snapshot", slot);
let bank_file_path = path.join(bank_file);
bank_index.insert(*slot, bank_file_path.clone());
let file = File::create(bank_file_path).unwrap();
let mut stream = BufWriter::new(file);
bank.serialize_into(&mut stream).unwrap();
}
let temp_path = path.join("bank.index.tmp");
{
let file = File::create(temp_path.clone()).unwrap();
let mut stream = BufWriter::new(file);
serialize_object(&bank_index, &mut stream).unwrap();
let banks = self.frozen_banks();
if !banks.is_empty() {
banks
.values()
.next()
.unwrap()
.accounts()
.serialize_into(&mut stream)
.unwrap();
}
}
fs::rename(temp_path, path.join("bank.index")).unwrap();
Ok(())
}

pub fn load_from_snapshot(path: &str) -> Result<Self, ()> {
let (bank_index, accounts) = {
let path = Path::new(path).join("bank.index");
let index_file = File::open(path).unwrap();
let mut stream = BufReader::new(index_file);
let bank_index: HashMap<u64, PathBuf> = deserialize_object(&mut stream).unwrap();
assert!(!bank_index.is_empty());
let accounts = Arc::new(Accounts::deserialize_from(&mut stream).unwrap());
(bank_index, accounts)
};

let mut banks: HashMap<u64, Arc<Bank>> = HashMap::new();
for (slot, bank_path) in &bank_index {
let file = File::open(bank_path).unwrap();
let mut stream = BufReader::new(file);
let mut bank = Bank::deserialize_from(&mut stream).unwrap();
bank.set_accounts(accounts.clone());
banks.insert(*slot, Arc::new(bank));
}
let working_bank = banks[&0].clone();
Ok(BankForks {
banks,
working_bank,
})
}
}

#[cfg(test)]
Expand Down Expand Up @@ -129,4 +193,23 @@ mod tests {
assert_eq!(bank_forks.active_banks(), vec![1]);
}

#[test]
fn test_bank_forks_snapshot() {
solana_logger::setup();
let (genesis_block, _) = GenesisBlock::new(10_000);
let bank = Bank::new(&genesis_block);
bank.freeze();
let tick_height = bank.tick_height();
let mut bank_forks = BankForks::new(0, bank);
let child_bank = Bank::new_from_parent(&bank_forks[0u64], Pubkey::default(), 1);
child_bank.freeze();
bank_forks.insert(1, child_bank);
let snapshot_path = "snapshots";
bank_forks.save_snapshot(snapshot_path).unwrap();
drop(bank_forks);
let new = BankForks::load_from_snapshot(snapshot_path).unwrap();
assert_eq!(new[0].tick_height(), tick_height);
let _ = fs::remove_dir_all(snapshot_path);
}

}
17 changes: 14 additions & 3 deletions core/src/fullnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub struct FullnodeConfig {
pub tick_config: PohServiceConfig,
pub account_paths: Option<String>,
pub rpc_config: JsonRpcConfig,
pub use_snapshot: bool,
}
impl Default for FullnodeConfig {
fn default() -> Self {
Expand All @@ -58,6 +59,7 @@ impl Default for FullnodeConfig {
tick_config: PohServiceConfig::default(),
account_paths: None,
rpc_config: JsonRpcConfig::default(),
use_snapshot: false,
}
}
}
Expand Down Expand Up @@ -93,7 +95,11 @@ impl Fullnode {
assert_eq!(id, node.info.id);

let (bank_forks, bank_forks_info, blocktree, ledger_signal_receiver) =
new_banks_from_blocktree(ledger_path, config.account_paths.clone());
new_banks_from_blocktree(
ledger_path,
config.account_paths.clone(),
config.use_snapshot,
);

let exit = Arc::new(AtomicBool::new(false));
let bank_info = &bank_forks_info[0];
Expand Down Expand Up @@ -266,6 +272,7 @@ impl Fullnode {
pub fn new_banks_from_blocktree(
blocktree_path: &str,
account_paths: Option<String>,
use_snapshot: bool,
) -> (BankForks, Vec<BankForksInfo>, Blocktree, Receiver<bool>) {
let genesis_block =
GenesisBlock::load(blocktree_path).expect("Expected to successfully open genesis block");
Expand All @@ -274,9 +281,13 @@ pub fn new_banks_from_blocktree(
Blocktree::open_with_config_signal(blocktree_path, genesis_block.ticks_per_slot)
.expect("Expected to successfully open database ledger");

let (bank_forks, bank_forks_info) =
let (bank_forks, bank_forks_info) = if use_snapshot {
let bank_forks = BankForks::load_from_snapshot("bank-snapshot").unwrap();
(bank_forks, vec![])
} else {
blocktree_processor::process_blocktree(&genesis_block, &blocktree, account_paths)
.expect("process_blocktree failed");
.expect("process_blocktree failed")
};

(
bank_forks,
Expand Down
2 changes: 1 addition & 1 deletion core/src/replay_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ mod test {
{
let voting_keypair = Arc::new(Keypair::new());
let (bank_forks, _bank_forks_info, blocktree, l_receiver) =
new_banks_from_blocktree(&my_ledger_path, None);
new_banks_from_blocktree(&my_ledger_path, None, false);
let bank = bank_forks.working_bank();

let blocktree = Arc::new(blocktree);
Expand Down
2 changes: 1 addition & 1 deletion core/src/staking_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ fn node_staked_accounts_at_epoch(
epoch_height: u64,
) -> Option<impl Iterator<Item = (&Pubkey, u64, &Account)>> {
bank.epoch_vote_accounts(epoch_height).map(|epoch_state| {
epoch_state.into_iter().filter_map(|(account_id, account)| {
epoch_state.iter().filter_map(|(account_id, account)| {
filter_zero_balances(account).map(|stake| (account_id, stake, account))
})
})
Expand Down
1 change: 1 addition & 0 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ solana-system-program = { path = "../programs/system", version = "0.12.0" }
solana-storage-api = { path = "../programs/storage_api", version = "0.12.0" }
solana-token-api = { path = "../programs/token_api", version = "0.12.0" }
solana-vote-api = { path = "../programs/vote_api", version = "0.12.0" }
bitintr = "0.2.0"

[lib]
name = "solana_runtime"
Expand Down
Loading