Skip to content

Commit

Permalink
Be able to create bank snapshots
Browse files Browse the repository at this point in the history
  • Loading branch information
sakridge committed Mar 9, 2019
1 parent 6de24ff commit fd3c89b
Show file tree
Hide file tree
Showing 14 changed files with 566 additions and 62 deletions.
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

0 comments on commit fd3c89b

Please sign in to comment.