[TieredStorage] In-memory struct for writing OwnersBlock (#34853)

#### Problem
To write the owners-block, it requires an in-memory struct that maintains
a set of unique owner addresses while providing a look-up function to
obtain the OwnerOffset with the specified owner address. 

#### Summary of Changes
This PR adds OwnersTable, the in-memory struct that maintains
a set of unique owner addresses while providing a look-up function to
obtain the OwnerOffset with the specified owner address.

#### Test Plan
A new unit-test is added.
This commit is contained in:
Yueh-Hsuan Chiang 2024-01-23 14:57:53 -08:00 committed by GitHub
parent 3303c2566c
commit 1810feadc2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 76 additions and 8 deletions

1
Cargo.lock generated
View File

@ -5307,6 +5307,7 @@ dependencies = [
"fnv",
"im",
"index_list",
"indexmap 2.1.0",
"itertools",
"lazy_static",
"libsecp256k1",

View File

@ -23,6 +23,7 @@ flate2 = { workspace = true }
fnv = { workspace = true }
im = { workspace = true, features = ["rayon", "serde"] }
index_list = { workspace = true }
indexmap = { workspace = true }
itertools = { workspace = true }
lazy_static = { workspace = true }
log = { workspace = true }

View File

@ -470,7 +470,7 @@ pub mod tests {
hot::{HotAccountMeta, HotStorageReader},
index::{AccountIndexWriterEntry, IndexBlockFormat, IndexOffset},
meta::{AccountMetaFlags, AccountMetaOptionalFields, TieredAccountMeta},
owners::OwnersBlockFormat,
owners::{OwnersBlockFormat, OwnersTable},
},
assert_matches::assert_matches,
memoffset::offset_of,
@ -823,9 +823,13 @@ pub mod tests {
{
let file = TieredStorageFile::new_writable(&path).unwrap();
let mut owners_table = OwnersTable::default();
addresses.iter().for_each(|owner_address| {
owners_table.insert(owner_address);
});
footer
.owners_block_format
.write_owners_block(&file, &addresses)
.write_owners_block(&file, &owners_table)
.unwrap();
// while the test only focuses on account metas, writing a footer
@ -893,9 +897,13 @@ pub mod tests {
// the owners_block_offset set to the end of the accounts blocks.
footer.owners_block_offset = footer.index_block_offset;
let mut owners_table = OwnersTable::default();
owner_addresses.iter().for_each(|owner_address| {
owners_table.insert(owner_address);
});
footer
.owners_block_format
.write_owners_block(&file, &owner_addresses)
.write_owners_block(&file, &owners_table)
.unwrap();
// while the test only focuses on account metas, writing a footer
@ -1029,9 +1037,13 @@ pub mod tests {
// write owners block
footer.owners_block_offset = current_offset as u64;
let mut owners_table = OwnersTable::default();
owners.iter().for_each(|owner_address| {
owners_table.insert(owner_address);
});
footer
.owners_block_format
.write_owners_block(&file, &owners)
.write_owners_block(&file, &owners_table)
.unwrap();
footer.write_footer_block(&file).unwrap();

View File

@ -3,6 +3,7 @@ use {
file::TieredStorageFile, footer::TieredStorageFooter, mmap_utils::get_pod,
TieredStorageResult,
},
indexmap::set::IndexSet,
memmap2::Mmap,
solana_sdk::pubkey::Pubkey,
};
@ -43,13 +44,13 @@ impl OwnersBlockFormat {
pub fn write_owners_block(
&self,
file: &TieredStorageFile,
addresses: &[Pubkey],
owners_table: &OwnersTable,
) -> TieredStorageResult<usize> {
match self {
Self::AddressesOnly => {
let mut bytes_written = 0;
for address in addresses {
bytes_written += file.write_pod(address)?;
for address in &owners_table.owners_set {
bytes_written += file.write_pod(*address)?;
}
Ok(bytes_written)
@ -77,6 +78,27 @@ impl OwnersBlockFormat {
}
}
/// The in-memory representation of owners block for write.
/// It manages a set of unique addresses of account owners.
#[derive(Debug, Default)]
pub struct OwnersTable<'a> {
owners_set: IndexSet<&'a Pubkey>,
}
/// OwnersBlock is persisted as a consecutive bytes of pubkeys without any
/// meta-data. For each account meta, it has a owner_offset field to
/// access its owner's address in the OwnersBlock.
impl<'a> OwnersTable<'a> {
/// Add the specified pubkey as the owner into the OwnersWriterTable
/// if the specified pubkey has not existed in the OwnersWriterTable
/// yet. In any case, the function returns its OwnerOffset.
pub fn insert(&mut self, pubkey: &'a Pubkey) -> OwnerOffset {
let (offset, _existed) = self.owners_set.insert_full(pubkey);
OwnerOffset(offset as u32)
}
}
#[cfg(test)]
mod tests {
use {
@ -105,9 +127,13 @@ mod tests {
{
let file = TieredStorageFile::new_writable(&path).unwrap();
let mut owners_table = OwnersTable::default();
addresses.iter().for_each(|owner_address| {
owners_table.insert(owner_address);
});
footer
.owners_block_format
.write_owners_block(&file, &addresses)
.write_owners_block(&file, &owners_table)
.unwrap();
// while the test only focuses on account metas, writing a footer
@ -128,4 +154,31 @@ mod tests {
);
}
}
#[test]
fn test_owners_table() {
let mut owners_table = OwnersTable::default();
const NUM_OWNERS: usize = 99;
let addresses: Vec<_> = std::iter::repeat_with(Pubkey::new_unique)
.take(NUM_OWNERS)
.collect();
// as we insert sequentially, we expect each entry has same OwnerOffset
// as its index inside the Vector.
for (i, address) in addresses.iter().enumerate() {
assert_eq!(owners_table.insert(address), OwnerOffset(i as u32));
}
let cloned_addresses = addresses.clone();
// insert again and expect the same OwnerOffset
for (i, address) in cloned_addresses.iter().enumerate() {
assert_eq!(owners_table.insert(address), OwnerOffset(i as u32));
}
// make sure the size of the resulting owner table is the same
// as the input
assert_eq!(owners_table.owners_set.len(), addresses.len());
}
}

View File

@ -4672,6 +4672,7 @@ dependencies = [
"fnv",
"im",
"index_list",
"indexmap 2.1.0",
"itertools",
"lazy_static",
"log",