Only mmap file from snapshot once (#14815)
This commit is contained in:
parent
2ca0872a98
commit
a53b8558cd
|
@ -381,13 +381,18 @@ impl AccountStorageEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_empty_map(id: AppendVecId, accounts_current_len: usize) -> Self {
|
pub(crate) fn new_existing(
|
||||||
|
slot: Slot,
|
||||||
|
id: AppendVecId,
|
||||||
|
accounts: AppendVec,
|
||||||
|
num_accounts: usize,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: AtomicUsize::new(id),
|
id: AtomicUsize::new(id),
|
||||||
slot: AtomicU64::new(0),
|
slot: AtomicU64::new(slot),
|
||||||
accounts: AppendVec::new_empty_map(accounts_current_len),
|
accounts,
|
||||||
count_and_status: RwLock::new((0, AccountStorageStatus::Available)),
|
count_and_status: RwLock::new((0, AccountStorageStatus::Available)),
|
||||||
approx_store_count: AtomicUsize::new(0),
|
approx_store_count: AtomicUsize::new(num_accounts),
|
||||||
alive_bytes: AtomicUsize::new(0),
|
alive_bytes: AtomicUsize::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -521,13 +526,6 @@ impl AccountStorageEntry {
|
||||||
count
|
count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_file<P: AsRef<Path>>(&mut self, path: P) -> IOResult<()> {
|
|
||||||
let num_accounts = self.accounts.set_file(path)?;
|
|
||||||
self.approx_store_count
|
|
||||||
.store(num_accounts, Ordering::Relaxed);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_relative_path(&self) -> Option<PathBuf> {
|
pub fn get_relative_path(&self) -> Option<PathBuf> {
|
||||||
AppendVec::get_relative_path(self.accounts.get_path())
|
AppendVec::get_relative_path(self.accounts.get_path())
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ impl<'a> StoredAccountMeta<'a> {
|
||||||
|
|
||||||
fn ref_executable_byte(&self) -> &u8 {
|
fn ref_executable_byte(&self) -> &u8 {
|
||||||
// Use extra references to avoid value silently clamped to 1 (=true) and 0 (=false)
|
// Use extra references to avoid value silently clamped to 1 (=true) and 0 (=false)
|
||||||
// Yes, this really happens; see test_set_file_crafted_executable
|
// Yes, this really happens; see test_new_from_file_crafted_executable
|
||||||
let executable_bool: &bool = &self.account_meta.executable;
|
let executable_bool: &bool = &self.account_meta.executable;
|
||||||
// UNSAFE: Force to interpret mmap-backed bool as u8 to really read the actual memory content
|
// UNSAFE: Force to interpret mmap-backed bool as u8 to really read the actual memory content
|
||||||
let executable_byte: &u8 = unsafe { &*(executable_bool as *const bool as *const u8) };
|
let executable_byte: &u8 = unsafe { &*(executable_bool as *const bool as *const u8) };
|
||||||
|
@ -271,29 +271,28 @@ impl AppendVec {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::mutex_atomic)]
|
#[allow(clippy::mutex_atomic)]
|
||||||
pub fn set_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<usize> {
|
pub fn new_from_file<P: AsRef<Path>>(path: P, current_len: usize) -> io::Result<(Self, usize)> {
|
||||||
// this AppendVec must not hold actual file;
|
|
||||||
assert_eq!(self.file_size, 0);
|
|
||||||
|
|
||||||
let data = OpenOptions::new()
|
let data = OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(false)
|
.create(false)
|
||||||
.open(&path)?;
|
.open(&path)?;
|
||||||
|
|
||||||
let current_len = self.current_len.load(Ordering::Relaxed);
|
|
||||||
assert_eq!(current_len, *self.append_offset.lock().unwrap());
|
|
||||||
|
|
||||||
let file_size = std::fs::metadata(&path)?.len();
|
let file_size = std::fs::metadata(&path)?.len();
|
||||||
AppendVec::sanitize_len_and_size(current_len, file_size as usize)?;
|
AppendVec::sanitize_len_and_size(current_len, file_size as usize)?;
|
||||||
|
|
||||||
let map = unsafe { MmapMut::map_mut(&data)? };
|
let map = unsafe { MmapMut::map_mut(&data)? };
|
||||||
|
|
||||||
self.file_size = file_size;
|
let new = AppendVec {
|
||||||
self.path = path.as_ref().to_path_buf();
|
path: path.as_ref().to_path_buf(),
|
||||||
self.map = map;
|
map,
|
||||||
|
append_offset: Mutex::new(current_len),
|
||||||
|
current_len: AtomicUsize::new(current_len),
|
||||||
|
file_size,
|
||||||
|
remove_on_drop: true,
|
||||||
|
};
|
||||||
|
|
||||||
let (sanitized, num_accounts) = self.sanitize_layout_and_length();
|
let (sanitized, num_accounts) = new.sanitize_layout_and_length();
|
||||||
if !sanitized {
|
if !sanitized {
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::Other,
|
std::io::ErrorKind::Other,
|
||||||
|
@ -301,7 +300,7 @@ impl AppendVec {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(num_accounts)
|
Ok((new, num_accounts))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sanitize_layout_and_length(&self) -> (bool, usize) {
|
fn sanitize_layout_and_length(&self) -> (bool, usize) {
|
||||||
|
@ -564,11 +563,9 @@ pub mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_append_vec_set_file_bad_size() {
|
fn test_append_vec_new_from_file_bad_size() {
|
||||||
let file = get_append_vec_path("test_append_vec_set_file_bad_size");
|
let file = get_append_vec_path("test_append_vec_new_from_file_bad_size");
|
||||||
let path = &file.path;
|
let path = &file.path;
|
||||||
let mut av = AppendVec::new_empty_map(0);
|
|
||||||
assert_eq!(av.accounts(0).len(), 0);
|
|
||||||
|
|
||||||
let _data = OpenOptions::new()
|
let _data = OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
|
@ -577,7 +574,7 @@ pub mod tests {
|
||||||
.open(&path)
|
.open(&path)
|
||||||
.expect("create a test file for mmap");
|
.expect("create a test file for mmap");
|
||||||
|
|
||||||
let result = av.set_file(path);
|
let result = AppendVec::new_from_file(path, 0);
|
||||||
assert_matches!(result, Err(ref message) if message.to_string() == *"too small file size 0 for AppendVec");
|
assert_matches!(result, Err(ref message) if message.to_string() == *"too small file size 0 for AppendVec");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -693,10 +690,11 @@ pub mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_file_crafted_zero_lamport_account() {
|
fn test_new_from_file_crafted_zero_lamport_account() {
|
||||||
let file = get_append_vec_path("test_append");
|
let file = get_append_vec_path("test_append");
|
||||||
let path = &file.path;
|
let path = &file.path;
|
||||||
let mut av = AppendVec::new(&path, true, 1024 * 1024);
|
let mut av = AppendVec::new(&path, true, 1024 * 1024);
|
||||||
|
av.set_no_remove_on_drop();
|
||||||
|
|
||||||
let pubkey = solana_sdk::pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let owner = Pubkey::default();
|
let owner = Pubkey::default();
|
||||||
|
@ -713,16 +711,18 @@ pub mod tests {
|
||||||
assert_eq!(av.get_account_test(index).unwrap(), account_with_meta);
|
assert_eq!(av.get_account_test(index).unwrap(), account_with_meta);
|
||||||
|
|
||||||
av.flush().unwrap();
|
av.flush().unwrap();
|
||||||
av.file_size = 0;
|
let accounts_len = av.len();
|
||||||
let result = av.set_file(path);
|
drop(av);
|
||||||
|
let result = AppendVec::new_from_file(path, accounts_len);
|
||||||
assert_matches!(result, Err(ref message) if message.to_string() == *"incorrect layout/length/data");
|
assert_matches!(result, Err(ref message) if message.to_string() == *"incorrect layout/length/data");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_file_crafted_data_len() {
|
fn test_new_from_file_crafted_data_len() {
|
||||||
let file = get_append_vec_path("test_set_file_crafted_data_len");
|
let file = get_append_vec_path("test_new_from_file_crafted_data_len");
|
||||||
let path = &file.path;
|
let path = &file.path;
|
||||||
let mut av = AppendVec::new(&path, true, 1024 * 1024);
|
let mut av = AppendVec::new(&path, true, 1024 * 1024);
|
||||||
|
av.set_no_remove_on_drop();
|
||||||
|
|
||||||
let crafted_data_len = 1;
|
let crafted_data_len = 1;
|
||||||
|
|
||||||
|
@ -739,16 +739,18 @@ pub mod tests {
|
||||||
assert_eq!(account.meta.data_len, crafted_data_len);
|
assert_eq!(account.meta.data_len, crafted_data_len);
|
||||||
|
|
||||||
av.flush().unwrap();
|
av.flush().unwrap();
|
||||||
av.file_size = 0;
|
let accounts_len = av.len();
|
||||||
let result = av.set_file(path);
|
drop(av);
|
||||||
|
let result = AppendVec::new_from_file(path, accounts_len);
|
||||||
assert_matches!(result, Err(ref message) if message.to_string() == *"incorrect layout/length/data");
|
assert_matches!(result, Err(ref message) if message.to_string() == *"incorrect layout/length/data");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_file_too_large_data_len() {
|
fn test_new_from_file_too_large_data_len() {
|
||||||
let file = get_append_vec_path("test_set_file_too_large_data_len");
|
let file = get_append_vec_path("test_new_from_file_too_large_data_len");
|
||||||
let path = &file.path;
|
let path = &file.path;
|
||||||
let mut av = AppendVec::new(&path, true, 1024 * 1024);
|
let mut av = AppendVec::new(&path, true, 1024 * 1024);
|
||||||
|
av.set_no_remove_on_drop();
|
||||||
|
|
||||||
let too_large_data_len = u64::max_value();
|
let too_large_data_len = u64::max_value();
|
||||||
av.append_account_test(&create_test_account(10)).unwrap();
|
av.append_account_test(&create_test_account(10)).unwrap();
|
||||||
|
@ -763,16 +765,18 @@ pub mod tests {
|
||||||
assert_matches!(accounts.first(), None);
|
assert_matches!(accounts.first(), None);
|
||||||
|
|
||||||
av.flush().unwrap();
|
av.flush().unwrap();
|
||||||
av.file_size = 0;
|
let accounts_len = av.len();
|
||||||
let result = av.set_file(path);
|
drop(av);
|
||||||
|
let result = AppendVec::new_from_file(path, accounts_len);
|
||||||
assert_matches!(result, Err(ref message) if message.to_string() == *"incorrect layout/length/data");
|
assert_matches!(result, Err(ref message) if message.to_string() == *"incorrect layout/length/data");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_file_crafted_executable() {
|
fn test_new_from_file_crafted_executable() {
|
||||||
let file = get_append_vec_path("test_set_file_crafted_executable");
|
let file = get_append_vec_path("test_new_from_crafted_executable");
|
||||||
let path = &file.path;
|
let path = &file.path;
|
||||||
let mut av = AppendVec::new(&path, true, 1024 * 1024);
|
let mut av = AppendVec::new(&path, true, 1024 * 1024);
|
||||||
|
av.set_no_remove_on_drop();
|
||||||
av.append_account_test(&create_test_account(10)).unwrap();
|
av.append_account_test(&create_test_account(10)).unwrap();
|
||||||
{
|
{
|
||||||
let mut executable_account = create_test_account(10);
|
let mut executable_account = create_test_account(10);
|
||||||
|
@ -817,8 +821,9 @@ pub mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
av.flush().unwrap();
|
av.flush().unwrap();
|
||||||
av.file_size = 0;
|
let accounts_len = av.len();
|
||||||
let result = av.set_file(path);
|
drop(av);
|
||||||
|
let result = AppendVec::new_from_file(path, accounts_len);
|
||||||
assert_matches!(result, Err(ref message) if message.to_string() == *"incorrect layout/length/data");
|
assert_matches!(result, Err(ref message) if message.to_string() == *"incorrect layout/length/data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use {
|
||||||
epoch_stakes::EpochStakes,
|
epoch_stakes::EpochStakes,
|
||||||
message_processor::MessageProcessor,
|
message_processor::MessageProcessor,
|
||||||
rent_collector::RentCollector,
|
rent_collector::RentCollector,
|
||||||
|
serde_snapshot::future::SerializableStorage,
|
||||||
stakes::Stakes,
|
stakes::Stakes,
|
||||||
},
|
},
|
||||||
bincode,
|
bincode,
|
||||||
|
@ -70,7 +71,7 @@ trait TypeContext<'a> {
|
||||||
type SerializableAccountStorageEntry: Serialize
|
type SerializableAccountStorageEntry: Serialize
|
||||||
+ DeserializeOwned
|
+ DeserializeOwned
|
||||||
+ From<&'a AccountStorageEntry>
|
+ From<&'a AccountStorageEntry>
|
||||||
+ Into<AccountStorageEntry>;
|
+ SerializableStorage;
|
||||||
|
|
||||||
fn serialize_bank_and_storage<S: serde::ser::Serializer>(
|
fn serialize_bank_and_storage<S: serde::ser::Serializer>(
|
||||||
serializer: S,
|
serializer: S,
|
||||||
|
@ -240,7 +241,7 @@ fn reconstruct_bank_from_fields<E, P>(
|
||||||
caching_enabled: bool,
|
caching_enabled: bool,
|
||||||
) -> Result<Bank, Error>
|
) -> Result<Bank, Error>
|
||||||
where
|
where
|
||||||
E: Into<AccountStorageEntry>,
|
E: SerializableStorage,
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let mut accounts_db = reconstruct_accountsdb_from_fields(
|
let mut accounts_db = reconstruct_accountsdb_from_fields(
|
||||||
|
@ -274,7 +275,7 @@ fn reconstruct_accountsdb_from_fields<E, P>(
|
||||||
caching_enabled: bool,
|
caching_enabled: bool,
|
||||||
) -> Result<AccountsDB, Error>
|
) -> Result<AccountsDB, Error>
|
||||||
where
|
where
|
||||||
E: Into<AccountStorageEntry>,
|
E: SerializableStorage,
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let mut accounts_db = AccountsDB::new_with_config(
|
let mut accounts_db = AccountsDB::new_with_config(
|
||||||
|
@ -285,20 +286,6 @@ where
|
||||||
);
|
);
|
||||||
let AccountsDbFields(storage, version, slot, bank_hash_info) = accounts_db_fields;
|
let AccountsDbFields(storage, version, slot, bank_hash_info) = accounts_db_fields;
|
||||||
|
|
||||||
// convert to two level map of slot -> id -> account storage entry
|
|
||||||
let storage = {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
for (slot, entries) in storage.into_iter() {
|
|
||||||
let sub_map = map.entry(slot).or_insert_with(HashMap::new);
|
|
||||||
for entry in entries.into_iter() {
|
|
||||||
let entry: AccountStorageEntry = entry.into();
|
|
||||||
entry.slot.store(slot, Ordering::Relaxed);
|
|
||||||
sub_map.insert(entry.append_vec_id(), Arc::new(entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
map
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure all account paths exist
|
// Ensure all account paths exist
|
||||||
for path in &accounts_db.paths {
|
for path in &accounts_db.paths {
|
||||||
std::fs::create_dir_all(path)
|
std::fs::create_dir_all(path)
|
||||||
|
@ -320,14 +307,14 @@ where
|
||||||
remaining_slots_to_process -= 1;
|
remaining_slots_to_process -= 1;
|
||||||
|
|
||||||
let mut new_slot_storage = HashMap::new();
|
let mut new_slot_storage = HashMap::new();
|
||||||
for (id, storage_entry) in slot_storage.drain() {
|
for storage_entry in slot_storage.drain(..) {
|
||||||
let path_index = thread_rng().gen_range(0, accounts_db.paths.len());
|
let path_index = thread_rng().gen_range(0, accounts_db.paths.len());
|
||||||
let local_dir = &accounts_db.paths[path_index];
|
let local_dir = &accounts_db.paths[path_index];
|
||||||
|
|
||||||
// Move the corresponding AppendVec from the snapshot into the directory pointed
|
// Move the corresponding AppendVec from the snapshot into the directory pointed
|
||||||
// at by `local_dir`
|
// at by `local_dir`
|
||||||
let append_vec_relative_path =
|
let append_vec_relative_path =
|
||||||
AppendVec::new_relative_path(slot, storage_entry.append_vec_id());
|
AppendVec::new_relative_path(slot, storage_entry.id());
|
||||||
let append_vec_abs_path = stream_append_vecs_path
|
let append_vec_abs_path = stream_append_vecs_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.join(&append_vec_relative_path);
|
.join(&append_vec_relative_path);
|
||||||
|
@ -342,9 +329,17 @@ where
|
||||||
|
|
||||||
// Notify the AppendVec of the new file location
|
// Notify the AppendVec of the new file location
|
||||||
let local_path = local_dir.join(append_vec_relative_path);
|
let local_path = local_dir.join(append_vec_relative_path);
|
||||||
let mut u_storage_entry = Arc::try_unwrap(storage_entry).unwrap();
|
|
||||||
u_storage_entry.set_file(local_path)?;
|
let (accounts, num_accounts) =
|
||||||
new_slot_storage.insert(id, Arc::new(u_storage_entry));
|
AppendVec::new_from_file(&local_path, storage_entry.current_len())?;
|
||||||
|
let u_storage_entry = AccountStorageEntry::new_existing(
|
||||||
|
slot,
|
||||||
|
storage_entry.id(),
|
||||||
|
accounts,
|
||||||
|
num_accounts,
|
||||||
|
);
|
||||||
|
|
||||||
|
new_slot_storage.insert(storage_entry.id(), Arc::new(u_storage_entry));
|
||||||
}
|
}
|
||||||
Ok((slot, new_slot_storage))
|
Ok((slot, new_slot_storage))
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,6 +12,20 @@ pub(super) struct SerializableAccountStorageEntry {
|
||||||
accounts_current_len: usize,
|
accounts_current_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait SerializableStorage {
|
||||||
|
fn id(&self) -> AppendVecId;
|
||||||
|
fn current_len(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerializableStorage for SerializableAccountStorageEntry {
|
||||||
|
fn id(&self) -> AppendVecId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
fn current_len(&self) -> usize {
|
||||||
|
self.accounts_current_len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(test, RUSTC_WITH_SPECIALIZATION))]
|
#[cfg(all(test, RUSTC_WITH_SPECIALIZATION))]
|
||||||
impl solana_frozen_abi::abi_example::IgnoreAsHelper for SerializableAccountStorageEntry {}
|
impl solana_frozen_abi::abi_example::IgnoreAsHelper for SerializableAccountStorageEntry {}
|
||||||
|
|
||||||
|
@ -24,12 +38,6 @@ impl From<&AccountStorageEntry> for SerializableAccountStorageEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SerializableAccountStorageEntry> for AccountStorageEntry {
|
|
||||||
fn from(s: SerializableAccountStorageEntry) -> Self {
|
|
||||||
AccountStorageEntry::new_empty_map(s.id, s.accounts_current_len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
// Deserializable version of Bank which need not be serializable,
|
// Deserializable version of Bank which need not be serializable,
|
||||||
// because it's handled by SerializableVersionedBank.
|
// because it's handled by SerializableVersionedBank.
|
||||||
|
|
|
@ -25,15 +25,13 @@ fn main() {
|
||||||
|
|
||||||
let file = value_t_or_exit!(matches, "file", String);
|
let file = value_t_or_exit!(matches, "file", String);
|
||||||
let len = value_t_or_exit!(matches, "len", usize);
|
let len = value_t_or_exit!(matches, "len", usize);
|
||||||
let mut store = AppendVec::new_empty_map(len);
|
let (mut store, num_accounts) = AppendVec::new_from_file(file, len).expect("should succeed");
|
||||||
store.set_no_remove_on_drop();
|
store.set_no_remove_on_drop();
|
||||||
store.set_file(file).expect("set_file failed");
|
|
||||||
let accounts = store.accounts(0);
|
|
||||||
info!(
|
info!(
|
||||||
"store: len: {} capacity: {} accounts: {}",
|
"store: len: {} capacity: {} accounts: {}",
|
||||||
store.len(),
|
store.len(),
|
||||||
store.capacity(),
|
store.capacity(),
|
||||||
accounts.len()
|
num_accounts,
|
||||||
);
|
);
|
||||||
for account in store.accounts(0) {
|
for account in store.accounts(0) {
|
||||||
info!(
|
info!(
|
||||||
|
|
Loading…
Reference in New Issue