Skip to content

Commit

Permalink
feat(core, node): Change storage of program memory pages to triple map (
Browse files Browse the repository at this point in the history
  • Loading branch information
gshep authored and mqxf committed Oct 26, 2023
1 parent b267eb9 commit b79aafe
Show file tree
Hide file tree
Showing 35 changed files with 799 additions and 175 deletions.
35 changes: 19 additions & 16 deletions common/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@

use super::*;

use gear_core::pages::{PageNumber, PageU32Size, WasmPage};
use gear_core::{
pages::{PageNumber, PageU32Size, WasmPage},
program::MemoryInfix,
};
use gear_wasm_instrument::parity_wasm::{self, elements::*};
use sp_io::hashing::blake2_256;
use sp_runtime::traits::Zero;
Expand Down Expand Up @@ -150,22 +153,22 @@ pub fn set_program<
.collect();
let pages_with_data = persistent_pages_data.keys().copied().collect();

let memory_infix = MemoryInfix::new(1u32);
let program = ActiveProgram {
allocations,
pages_with_data,
code_hash: code_id,
code_exports: Default::default(),
static_pages,
state: ProgramState::Initialized,
gas_reservation_map: GasReservationMap::default(),
expiration_block: Zero::zero(),
memory_infix,
};
for (page, page_buf) in persistent_pages_data {
ProgramStorage::set_program_page_data(program_id, page, page_buf);
ProgramStorage::set_program_page_data(program_id, memory_infix, page, page_buf);
}

ProgramStorage::add_program(
program_id,
ActiveProgram {
allocations,
pages_with_data,
code_hash: code_id,
code_exports: Default::default(),
static_pages,
state: ProgramState::Initialized,
gas_reservation_map: GasReservationMap::default(),
expiration_block: Zero::zero(),
},
)
.expect("benchmarking; program duplicates should not exist");
ProgramStorage::add_program(program_id, program)
.expect("benchmarking; program duplicates should not exist");
}
2 changes: 2 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ use gear_core::{
memory::PageBuf,
message::DispatchKind,
pages::{GearPage, WasmPage},
program::MemoryInfix,
reservation::GasReservationMap,
};
use primitive_types::H256;
Expand Down Expand Up @@ -275,6 +276,7 @@ pub struct ActiveProgram<BlockNumber: Copy + Saturating> {
pub allocations: BTreeSet<WasmPage>,
/// Set of gear pages numbers, which has data in storage.
pub pages_with_data: BTreeSet<GearPage>,
pub memory_infix: MemoryInfix,
pub gas_reservation_map: GasReservationMap,
pub code_hash: H256,
pub code_exports: BTreeSet<DispatchKind>,
Expand Down
52 changes: 26 additions & 26 deletions common/src/paused_program_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use super::{program_storage::MemoryMap, *};
use crate::storage::{AppendMapStorage, MapStorage, ValueStorage};
use crate::storage::{MapStorage, ValueStorage};
use gear_core::{
code::MAX_WASM_PAGE_COUNT,
pages::{GEAR_PAGE_SIZE, WASM_PAGE_SIZE},
Expand All @@ -27,7 +27,7 @@ use sp_io::hashing;

const SPLIT_COUNT: u16 = (WASM_PAGE_SIZE / GEAR_PAGE_SIZE) as u16 * MAX_WASM_PAGE_COUNT / 2;

pub type SessionId = u128;
pub type SessionId = u32;

// The entity helps to calculate hash of program's data and memory pages.
// Its structure designed that way to avoid memory allocation of more than MAX_POSSIBLE_ALLOCATION bytes.
Expand Down Expand Up @@ -78,6 +78,7 @@ pub struct ResumeSession<AccountId, BlockNumber> {
user: AccountId,
program_id: ProgramId,
allocations: BTreeSet<WasmPage>,
pages_with_data: BTreeSet<GearPage>,
code_hash: CodeId,
end_block: BlockNumber,
}
Expand All @@ -100,11 +101,6 @@ pub trait PausedProgramStorage: super::ProgramStorage {
Key = SessionId,
Value = ResumeSession<Self::AccountId, Self::BlockNumber>,
>;
type SessionMemoryPages: AppendMapStorage<
(GearPage, PageBuf),
SessionId,
Vec<(GearPage, PageBuf)>,
>;

/// Attempt to remove all items from all the associated maps.
fn reset() {
Expand Down Expand Up @@ -136,6 +132,7 @@ pub trait PausedProgramStorage: super::ProgramStorage {

let memory_pages = match Self::get_program_data_for_pages(
program_id,
program.memory_infix,
program.pages_with_data.iter(),
) {
Ok(memory_pages) => memory_pages,
Expand All @@ -147,7 +144,7 @@ pub trait PausedProgramStorage: super::ProgramStorage {
};

Self::waiting_init_remove(program_id);
Self::remove_program_pages(program_id);
Self::remove_program_pages(program_id, program.memory_infix);

Ok((program, memory_pages))
})?;
Expand Down Expand Up @@ -192,6 +189,7 @@ pub trait PausedProgramStorage: super::ProgramStorage {
user,
program_id,
allocations,
pages_with_data: Default::default(),
code_hash,
end_block,
});
Expand All @@ -211,6 +209,8 @@ pub trait PausedProgramStorage: super::ProgramStorage {
user: Self::AccountId,
memory_pages: Vec<(GearPage, PageBuf)>,
) -> Result<(), Self::Error> {
// TODO: #3447 additional check

Self::ResumeSessions::mutate(session_id, |maybe_session| {
let session = match maybe_session.as_mut() {
Some(s) if s.user == user => s,
Expand All @@ -219,8 +219,14 @@ pub trait PausedProgramStorage: super::ProgramStorage {
};

session.page_count += memory_pages.len() as u32;
for page in memory_pages {
Self::SessionMemoryPages::append(session_id, page);
for (page, page_buf) in memory_pages {
session.pages_with_data.insert(page);
Self::set_program_page_data(
session.program_id,
MemoryInfix::new(session_id),
page,
page_buf,
);
}

Ok(())
Expand Down Expand Up @@ -248,7 +254,7 @@ pub trait PausedProgramStorage: super::ProgramStorage {
};

// it means that the program has been already resumed within another session
Self::SessionMemoryPages::remove(session_id);
Self::remove_program_pages(session.program_id, MemoryInfix::new(session_id));
*maybe_session = None;

return Ok(result);
Expand All @@ -263,10 +269,12 @@ pub trait PausedProgramStorage: super::ProgramStorage {
return Err(Self::InternalError::program_code_not_found().into());
}

let memory_pages: MemoryMap = Self::SessionMemoryPages::get(&session_id)
.unwrap_or_default()
.into_iter()
.collect();
let memory_pages = Self::get_program_data_for_pages(
session.program_id,
MemoryInfix::new(session_id),
session.pages_with_data.iter(),
)
.unwrap_or_default();
let code_hash = session.code_hash.into_origin();
let item = Item::from((session.allocations.clone(), code_hash, memory_pages));
if item.hash() != hash {
Expand All @@ -293,6 +301,7 @@ pub trait PausedProgramStorage: super::ProgramStorage {
static_pages: code.static_pages(),
state: ProgramState::Initialized,
expiration_block,
memory_infix: MemoryInfix::new(session_id),
};

let program_id = session.program_id;
Expand All @@ -304,15 +313,6 @@ pub trait PausedProgramStorage: super::ProgramStorage {
// wipe all uploaded data out
*maybe_session = None;
Self::PausedProgramMap::remove(program_id);
Self::SessionMemoryPages::remove(session_id);

// set program memory pages
for (page, page_buf) in memory_pages {
Self::set_program_page_data(program_id, page, page_buf);
}
for (page, page_buf) in remaining_pages {
Self::set_program_page_data(program_id, page, page_buf);
}

// and finally start the program
Self::ProgramMap::insert(program_id, Program::Active(program));
Expand All @@ -324,8 +324,8 @@ pub trait PausedProgramStorage: super::ProgramStorage {
/// Remove all data created by a call to `resume_session_init`.
fn remove_resume_session(session_id: SessionId) -> Result<(), Self::Error> {
Self::ResumeSessions::mutate(session_id, |maybe_session| match maybe_session.take() {
Some(_) => {
Self::SessionMemoryPages::remove(session_id);
Some(session) => {
Self::remove_program_pages(session.program_id, MemoryInfix::new(session_id));

Ok(())
}
Expand Down
39 changes: 27 additions & 12 deletions common/src/program_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use super::*;
use crate::storage::{AppendMapStorage, DoubleMapStorage, MapStorage};
use crate::storage::{AppendMapStorage, MapStorage, TripleMapStorage};
use core::fmt::Debug;

/// Trait for ProgramStorage errors.
Expand Down Expand Up @@ -62,7 +62,12 @@ pub trait ProgramStorage {
type AccountId: Eq + PartialEq;

type ProgramMap: MapStorage<Key = ProgramId, Value = Program<Self::BlockNumber>>;
type MemoryPageMap: DoubleMapStorage<Key1 = ProgramId, Key2 = GearPage, Value = PageBuf>;
type MemoryPageMap: TripleMapStorage<
Key1 = ProgramId,
Key2 = MemoryInfix,
Key3 = GearPage,
Value = PageBuf,
>;
type WaitingInitMap: AppendMapStorage<MessageId, ProgramId, Vec<MessageId>>;

/// Attempt to remove all items from all the associated maps.
Expand Down Expand Up @@ -136,31 +141,41 @@ pub trait ProgramStorage {
/// Return program data for each page from `pages`.
fn get_program_data_for_pages<'a>(
program_id: ProgramId,
memory_infix: MemoryInfix,
pages: impl Iterator<Item = &'a GearPage>,
) -> Result<MemoryMap, Self::Error> {
let mut pages_data = BTreeMap::new();
for page in pages {
let data = Self::MemoryPageMap::get(&program_id, page)
let data = Self::MemoryPageMap::get(&program_id, &memory_infix, page)
.ok_or(Self::InternalError::cannot_find_page_data())?;
pages_data.insert(*page, data);
}

Ok(pages_data)
}

/// Store a memory page buffer to be associated with the given keys `program_id` and `page` from the map.
fn set_program_page_data(program_id: ProgramId, page: GearPage, page_buf: PageBuf) {
Self::MemoryPageMap::insert(program_id, page, page_buf);
/// Store a memory page buffer to be associated with the given keys `program_id`, `memory_infix` and `page` from the map.
fn set_program_page_data(
program_id: ProgramId,
memory_infix: MemoryInfix,
page: GearPage,
page_buf: PageBuf,
) {
Self::MemoryPageMap::insert(program_id, memory_infix, page, page_buf);
}

/// Remove a memory page buffer under the given keys `program_id` and `page`.
fn remove_program_page_data(program_id: ProgramId, page_num: GearPage) {
Self::MemoryPageMap::remove(program_id, page_num);
/// Remove a memory page buffer under the given keys `program_id`, `memory_infix` and `page`.
fn remove_program_page_data(
program_id: ProgramId,
memory_infix: MemoryInfix,
page_num: GearPage,
) {
Self::MemoryPageMap::remove(program_id, memory_infix, page_num);
}

/// Remove all memory page buffers under the given key `program_id`.
fn remove_program_pages(program_id: ProgramId) {
Self::MemoryPageMap::clear_prefix(program_id);
/// Remove all memory page buffers under the given keys `program_id` and `memory_infix`.
fn remove_program_pages(program_id: ProgramId, memory_infix: MemoryInfix) {
Self::MemoryPageMap::clear_prefix(program_id, memory_infix);
}

/// Final full prefix that prefixes all keys of memory pages.
Expand Down
2 changes: 2 additions & 0 deletions common/src/storage/primitives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mod double_map;
mod iterable;
mod key;
mod map;
mod triple_map;
mod value;

// Public exports from primitive modules.
Expand All @@ -41,6 +42,7 @@ pub use iterable::{
};
pub use key::{KeyFor, MailboxKeyGen, QueueKeyGen, WaitlistKeyGen};
pub use map::{AppendMapStorage, MapStorage};
pub use triple_map::TripleMapStorage;
pub use value::ValueStorage;

use frame_support::{
Expand Down
Loading

0 comments on commit b79aafe

Please sign in to comment.