diff --git a/Cargo.lock b/Cargo.lock index 29dd53f79ff..09e289913c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10456,6 +10456,7 @@ version = "0.8.0" dependencies = [ "hex", "ic-base-types", + "ic-config", "ic-ic00-types", "ic-interfaces", "ic-logger", diff --git a/rs/config/src/state_manager.rs b/rs/config/src/state_manager.rs index 313e31cf73a..8af771946d3 100644 --- a/rs/config/src/state_manager.rs +++ b/rs/config/src/state_manager.rs @@ -8,6 +8,9 @@ pub struct Config { /// A feature flag that enables/disables the file backed memory allocator. #[serde(default = "file_backed_memory_allocator_default")] pub file_backed_memory_allocator: FlagStatus, + /// A feature flag that enables/disables the log structure merge tree based storage + #[serde(default = "lsmt_storage_default")] + pub lsmt_storage: FlagStatus, } impl Config { @@ -15,6 +18,7 @@ impl Config { Self { state_root, file_backed_memory_allocator: file_backed_memory_allocator_default(), + lsmt_storage: lsmt_storage_default(), } } @@ -32,3 +36,7 @@ impl Config { fn file_backed_memory_allocator_default() -> FlagStatus { FlagStatus::Enabled } + +pub fn lsmt_storage_default() -> FlagStatus { + FlagStatus::Disabled +} diff --git a/rs/replicated_state/src/page_map.rs b/rs/replicated_state/src/page_map.rs index 839cedd979e..fc6216145f8 100644 --- a/rs/replicated_state/src/page_map.rs +++ b/rs/replicated_state/src/page_map.rs @@ -4,6 +4,7 @@ mod page_allocator; mod storage; pub use checkpoint::{CheckpointSerialization, MappingSerialization}; +use ic_config::flag_status::FlagStatus; use ic_sys::PageBytes; pub use ic_sys::{PageIndex, PAGE_SIZE}; use ic_utils::{deterministic_operations::deterministic_copy_from_slice, fs::write_all_vectored}; @@ -234,6 +235,22 @@ pub enum MemoryMapOrData<'a> { Data(&'a [u8]), } +/// For write operations, whether the delta should be written to a base file or an overlay file +pub enum PersistDestination { + BaseFile(PathBuf), + OverlayFile(PathBuf), +} + +impl PersistDestination { + /// Helper function to simplify the typical match statement to construct this enum + pub fn new(base_file: PathBuf, overlay_file: PathBuf, lsmt_storage: FlagStatus) -> Self { + match lsmt_storage { + FlagStatus::Enabled => PersistDestination::OverlayFile(overlay_file), + FlagStatus::Disabled => PersistDestination::BaseFile(base_file), + } + } +} + /// PageMap is a data structure that represents an image of a canister virtual /// memory. The memory is viewed as a collection of _pages_. `PageMap` uses /// 4KiB host OS pages to track the heap contents, not 64KiB Wasm pages. @@ -396,14 +413,26 @@ impl PageMap { /// Persists the heap delta contained in this page map to the specified /// destination. - pub fn persist_delta(&self, dst: &Path) -> Result<(), PersistenceError> { - self.persist_to_file(&self.page_delta, dst) + pub fn persist_delta(&self, dst: PersistDestination) -> Result<(), PersistenceError> { + match dst { + PersistDestination::BaseFile(dst) => self.persist_to_file(&self.page_delta, &dst), + PersistDestination::OverlayFile(_dst) => { + //TODO (IC-1306) + unimplemented!(); + } + } } /// Persists the unflushed delta contained in this page map to the specified /// destination. - pub fn persist_unflushed_delta(&self, dst: &Path) -> Result<(), PersistenceError> { - self.persist_to_file(&self.unflushed_delta, dst) + pub fn persist_unflushed_delta(&self, dst: PersistDestination) -> Result<(), PersistenceError> { + match dst { + PersistDestination::BaseFile(dst) => self.persist_to_file(&self.unflushed_delta, &dst), + PersistDestination::OverlayFile(_dst) => { + //TODO (IC-1306) + unimplemented!(); + } + } } /// Returns the iterator over host pages managed by this `PageMap`. diff --git a/rs/replicated_state/src/page_map/tests.rs b/rs/replicated_state/src/page_map/tests.rs index 4efa35de1a9..bc965775b76 100644 --- a/rs/replicated_state/src/page_map/tests.rs +++ b/rs/replicated_state/src/page_map/tests.rs @@ -1,10 +1,9 @@ use super::{ checkpoint::{Checkpoint, MappingSerialization}, page_allocator::PageAllocatorSerialization, - Buffer, FileDescriptor, PageAllocatorRegistry, PageIndex, PageMap, PageMapSerialization, -}; -use crate::page_map::{ - MemoryInstructions, MemoryMapOrData, TestPageAllocatorFileDescriptorImpl, WRITE_BUCKET_PAGES, + Buffer, FileDescriptor, MemoryInstructions, MemoryMapOrData, PageAllocatorRegistry, PageIndex, + PageMap, PageMapSerialization, PersistDestination, TestPageAllocatorFileDescriptorImpl, + WRITE_BUCKET_PAGES, }; use ic_sys::PAGE_SIZE; use ic_types::{Height, MAX_STABLE_MEMORY_IN_BYTES}; @@ -117,7 +116,9 @@ fn persisted_map_is_equivalent_to_the_original() { .map(|(idx, p)| (*idx, p)) .collect::>(), ); - pagemap.persist_delta(heap_file).unwrap(); + pagemap + .persist_delta(PersistDestination::BaseFile(heap_file.to_path_buf())) + .unwrap(); let persisted_map = PageMap::open( heap_file, &[], @@ -189,7 +190,9 @@ fn can_persist_and_load_an_empty_page_map() { let heap_file = tmp.path().join("heap"); let original_map = PageMap::new_for_testing(); - original_map.persist_delta(&heap_file).unwrap(); + original_map + .persist_delta(PersistDestination::BaseFile(heap_file.clone())) + .unwrap(); let persisted_map = PageMap::open( &heap_file, &[], @@ -422,7 +425,9 @@ fn get_memory_instructions_returns_deltas() { page_map.get_memory_instructions(range.clone(), range.clone(), 0) ); - page_map.persist_delta(&heap_file).unwrap(); + page_map + .persist_delta(PersistDestination::BaseFile(heap_file.clone())) + .unwrap(); let mut page_map = PageMap::open( &heap_file, @@ -478,7 +483,9 @@ fn get_memory_instructions_returns_deltas() { page_map.update(pages); // No trailing zero pages are serialized. - page_map.persist_delta(&heap_file).unwrap(); + page_map + .persist_delta(PersistDestination::BaseFile(heap_file.clone())) + .unwrap(); assert_eq!(25 * PAGE_SIZE as u64, heap_file.metadata().unwrap().len()); } diff --git a/rs/state_layout/BUILD.bazel b/rs/state_layout/BUILD.bazel index 6206e70add5..edbea0f3bb1 100644 --- a/rs/state_layout/BUILD.bazel +++ b/rs/state_layout/BUILD.bazel @@ -4,6 +4,7 @@ package(default_visibility = ["//visibility:public"]) DEPENDENCIES = [ # Keep sorted. + "//rs/config", "//rs/monitoring/logger", "//rs/monitoring/metrics", "//rs/protobuf", diff --git a/rs/state_layout/Cargo.toml b/rs/state_layout/Cargo.toml index 70d0c530e74..4dd62e413db 100644 --- a/rs/state_layout/Cargo.toml +++ b/rs/state_layout/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] hex = "0.4.2" ic-base-types = { path = "../types/base_types" } +ic-config = { path = "../config" } ic-ic00-types = { path = "../types/ic00_types" } ic-logger = { path = "../monitoring/logger" } ic-metrics = { path = "../monitoring/metrics" } diff --git a/rs/state_layout/src/state_layout.rs b/rs/state_layout/src/state_layout.rs index df84355dcfa..942943576c7 100644 --- a/rs/state_layout/src/state_layout.rs +++ b/rs/state_layout/src/state_layout.rs @@ -2,6 +2,7 @@ use crate::error::LayoutError; use crate::utils::do_copy; use ic_base_types::{NumBytes, NumSeconds}; +use ic_config::flag_status::FlagStatus; use ic_logger::{error, info, warn, ReplicaLogger}; use ic_metrics::{buckets::decimal_buckets, MetricsRegistry}; use ic_protobuf::{ @@ -316,6 +317,7 @@ impl TipHandler { &mut self, state_layout: &StateLayout, cp: &CheckpointLayout, + lsmt_storage: FlagStatus, thread_pool: Option<&mut scoped_threadpool::Pool>, ) -> Result<(), LayoutError> { let tip = self.tip_path(); @@ -331,13 +333,17 @@ impl TipHandler { let file_copy_instruction = |path: &Path| { if path.extension() == Some(OsStr::new("pbuf")) { - // Do not copy protobufs + // Do not copy protobufs. CopyInstruction::Skip - } else if path.extension() == Some(OsStr::new("bin")) { - // PageMap files need to be modified in the tip + } else if path.extension() == Some(OsStr::new("bin")) + && lsmt_storage == FlagStatus::Disabled + { + // PageMap files need to be modified in the tip, + // but only with non-LSMT storage layer that modifies these files. + // With LSMT we always write additional overlay files instead. CopyInstruction::ReadWrite } else { - // Everything else should be readonly + // Everything else should be readonly. CopyInstruction::ReadOnly } }; diff --git a/rs/state_manager/src/checkpoint.rs b/rs/state_manager/src/checkpoint.rs index 7b54b283fb2..4428407047d 100644 --- a/rs/state_manager/src/checkpoint.rs +++ b/rs/state_manager/src/checkpoint.rs @@ -1,8 +1,6 @@ use crate::{CheckpointError, CheckpointMetrics, TipRequest, NUMBER_OF_CHECKPOINT_THREADS}; use crossbeam_channel::{unbounded, Sender}; use ic_base_types::{subnet_id_try_from_protobuf, CanisterId}; -// TODO(MR-412): uncomment -//use ic_protobuf::proxy::try_from_option_field; use ic_registry_subnet_type::SubnetType; use ic_replicated_state::page_map::PageAllocatorFileDescriptor; use ic_replicated_state::Memory; diff --git a/rs/state_manager/src/checkpoint/tests.rs b/rs/state_manager/src/checkpoint/tests.rs index 642af6de00e..932a3b5c525 100644 --- a/rs/state_manager/src/checkpoint/tests.rs +++ b/rs/state_manager/src/checkpoint/tests.rs @@ -1,6 +1,7 @@ use super::*; use crate::{spawn_tip_thread, StateManagerMetrics, NUMBER_OF_CHECKPOINT_THREADS}; use ic_base_types::NumSeconds; +use ic_config::state_manager::lsmt_storage_default; use ic_ic00_types::CanisterStatusType; use ic_metrics::MetricsRegistry; use ic_registry_subnet_type::SubnetType; @@ -100,6 +101,7 @@ fn can_make_a_checkpoint() { log, tip_handler, layout.clone(), + lsmt_storage_default(), state_manager_metrics(), MaliciousFlags::default(), ); @@ -164,6 +166,7 @@ fn scratchpad_dir_is_deleted_if_checkpointing_failed() { log, tip_handler, layout, + lsmt_storage_default(), state_manager_metrics.clone(), MaliciousFlags::default(), ); @@ -214,6 +217,7 @@ fn can_recover_from_a_checkpoint() { log, tip_handler, layout.clone(), + lsmt_storage_default(), state_manager_metrics.clone(), MaliciousFlags::default(), ); @@ -316,6 +320,7 @@ fn can_recover_an_empty_state() { log, tip_handler, layout.clone(), + lsmt_storage_default(), state_manager_metrics.clone(), MaliciousFlags::default(), ); @@ -400,6 +405,7 @@ fn can_recover_a_stopping_canister() { log, tip_handler, layout.clone(), + lsmt_storage_default(), state_manager_metrics.clone(), MaliciousFlags::default(), ); @@ -467,6 +473,7 @@ fn can_recover_a_stopped_canister() { log, tip_handler, layout.clone(), + lsmt_storage_default(), state_manager_metrics.clone(), MaliciousFlags::default(), ); @@ -519,6 +526,7 @@ fn can_recover_a_running_canister() { log, tip_handler, layout.clone(), + lsmt_storage_default(), state_manager_metrics.clone(), MaliciousFlags::default(), ); @@ -571,6 +579,7 @@ fn can_recover_subnet_queues() { log, tip_handler, layout.clone(), + lsmt_storage_default(), state_manager_metrics.clone(), MaliciousFlags::default(), ); @@ -621,6 +630,7 @@ fn empty_protobufs_are_loaded_correctly() { log, tip_handler, layout.clone(), + lsmt_storage_default(), state_manager_metrics.clone(), MaliciousFlags::default(), ); diff --git a/rs/state_manager/src/lib.rs b/rs/state_manager/src/lib.rs index 202dc018984..a1f11f01d18 100644 --- a/rs/state_manager/src/lib.rs +++ b/rs/state_manager/src/lib.rs @@ -746,6 +746,7 @@ pub struct StateManagerImpl { fd_factory: Arc, malicious_flags: MaliciousFlags, latest_height_update_time: Arc>, + lsmt_storage: FlagStatus, } #[cfg(debug_assertions)] @@ -1028,6 +1029,39 @@ impl PageMapType { } } + /// The path of an overlay file written during round `height` + fn overlay( + &self, + layout: &CheckpointLayout, + height: Height, + ) -> Result + where + Access: AccessPolicy, + { + match &self { + PageMapType::WasmMemory(id) => Ok(layout.canister(id)?.vmemory_0_overlay(height)), + PageMapType::StableMemory(id) => Ok(layout.canister(id)?.stable_memory_overlay(height)), + PageMapType::WasmChunkStore(id) => { + Ok(layout.canister(id)?.wasm_chunk_store_overlay(height)) + } + } + } + + /// List all existing overlay files of a this PageMapType inside `layout` + fn overlays( + &self, + layout: &CheckpointLayout, + ) -> Result, LayoutError> + where + Access: AccessPolicy, + { + match &self { + PageMapType::WasmMemory(id) => layout.canister(id)?.vmemory_0_overlays(), + PageMapType::StableMemory(id) => layout.canister(id)?.stable_memory_overlays(), + PageMapType::WasmChunkStore(id) => layout.canister(id)?.wasm_chunk_store_overlays(), + } + } + /// Maps a PageMapType to the the `&PageMap` in `state` fn get<'a>(&self, state: &'a ReplicatedState) -> Option<&'a PageMap> { match &self { @@ -1365,6 +1399,7 @@ impl StateManagerImpl { log.clone(), state_layout.capture_tip_handler(), state_layout.clone(), + config.lsmt_storage, metrics.clone(), malicious_flags.clone(), ); @@ -1588,6 +1623,7 @@ impl StateManagerImpl { fd_factory, malicious_flags, latest_height_update_time: Arc::new(Mutex::new(Instant::now())), + lsmt_storage: config.lsmt_storage, } } /// Returns the Page Allocator file descriptor factory. This will then be @@ -3008,7 +3044,10 @@ impl StateManager for StateManagerImpl { checkpointed_state } CertificationScope::Metadata => { - if self.tip_channel.is_empty() { + if self.lsmt_storage == FlagStatus::Enabled { + // TODO (IC-1306): Implement LSMT strategy for when to flush page maps + unimplemented!(); + } else if self.tip_channel.is_empty() { self.flush_page_maps(&mut state, height); } else { self.metrics.checkpoint_metrics.page_map_flush_skips.inc(); diff --git a/rs/state_manager/src/split.rs b/rs/state_manager/src/split.rs index f799d8dc57e..39884d7baae 100644 --- a/rs/state_manager/src/split.rs +++ b/rs/state_manager/src/split.rs @@ -6,7 +6,7 @@ use crate::{ }; use ic_base_types::CanisterId; -use ic_config::state_manager::Config; +use ic_config::{flag_status::FlagStatus, state_manager::Config}; use ic_logger::ReplicaLogger; use ic_metrics::MetricsRegistry; use ic_registry_routing_table::{ @@ -178,15 +178,18 @@ fn write_checkpoint( log: ReplicaLogger, ) -> Result<(), String> { let old_height = old_cp.height(); + // As we do not write any new data, it does not matter if we use the LSMT storage layer or not + let lsmt_storage = FlagStatus::Disabled; let mut tip_handler = state_layout.capture_tip_handler(); tip_handler - .reset_tip_to(&state_layout, old_cp, Some(thread_pool)) + .reset_tip_to(&state_layout, old_cp, lsmt_storage, Some(thread_pool)) .map_err(|e| e.to_string())?; let (_tip_thread, tip_channel) = spawn_tip_thread( log, tip_handler, state_layout, + lsmt_storage, metrics.clone(), MaliciousFlags::default(), ); diff --git a/rs/state_manager/src/split/tests.rs b/rs/state_manager/src/split/tests.rs index 088bf65527c..02998e196bb 100644 --- a/rs/state_manager/src/split/tests.rs +++ b/rs/state_manager/src/split/tests.rs @@ -5,6 +5,7 @@ use crate::{ }; use assert_matches::assert_matches; use ic_base_types::{subnet_id_try_from_protobuf, CanisterId, NumSeconds}; +use ic_config::state_manager::lsmt_storage_default; use ic_error_types::{ErrorCode, UserError}; use ic_logger::ReplicaLogger; use ic_metrics::MetricsRegistry; @@ -327,6 +328,7 @@ fn new_state_layout(log: ReplicaLogger) -> (TempDir, Time) { log, tip_handler, layout.clone(), + lsmt_storage_default(), state_manager_metrics.clone(), MaliciousFlags::default(), ); diff --git a/rs/state_manager/src/tip.rs b/rs/state_manager/src/tip.rs index c236ee1d4ad..d06f0bd1c0c 100644 --- a/rs/state_manager/src/tip.rs +++ b/rs/state_manager/src/tip.rs @@ -5,11 +5,13 @@ use crate::{ }; use crossbeam_channel::{unbounded, Sender}; use ic_base_types::subnet_id_into_protobuf; +use ic_config::flag_status::FlagStatus; use ic_logger::{error, fatal, info, ReplicaLogger}; use ic_protobuf::state::{ stats::v1::Stats, system_metadata::v1::{SplitFrom, SystemMetadata}, }; +use ic_replicated_state::page_map::PersistDestination; #[allow(unused)] use ic_replicated_state::{ canister_state::execution_state::SandboxMemory, CanisterState, NumWasmPages, PageMap, @@ -96,7 +98,7 @@ pub(crate) enum TipRequest { height: Height, page_map_types: Vec, }, - /// Compute manifest, store result into states and perist metadata as result. + /// Compute manifest, store result into states and persist metadata as result. /// State: * ComputeManifest { checkpoint_layout: CheckpointLayout, @@ -120,25 +122,39 @@ fn request_timer(metrics: &StateManagerMetrics, name: &str) -> HistogramTimer { .start_timer() } -fn page_map_path( - log: &ReplicaLogger, +/// Helper struct for some relevant paths. Also see page_map_paths(). +struct PageMapPaths { + /// Path of the base file + base_file_path: PathBuf, + /// All existing overlay files + existing_overlays: Vec, + /// If we write a new overlay file, we use this path + next_overlay_path: PathBuf, +} + +/// Helper function to collect all relevant paths for a PageMap +fn page_map_paths( tip_handler: &mut TipHandler, height: Height, page_map_type: &PageMapType, -) -> PathBuf { - page_map_type - .path(&tip_handler.tip(height).unwrap_or_else(|err| { - fatal!(log, "Failed to flush page map: {}", err); - })) - .unwrap_or_else(|err| { - fatal!(log, "Failed to get path for page map: {}", err); - }) +) -> Result { + let layout = &tip_handler.tip(height)?; + let base_file_path = page_map_type.path(layout)?; + let existing_overlays = page_map_type.overlays(layout)?; + let next_overlay_path = page_map_type.overlay(layout, height)?; + + Ok(PageMapPaths { + base_file_path, + existing_overlays, + next_overlay_path, + }) } pub(crate) fn spawn_tip_thread( log: ReplicaLogger, mut tip_handler: TipHandler, state_layout: StateLayout, + lsmt_storage: FlagStatus, metrics: StateManagerMetrics, malicious_flags: MaliciousFlags, ) -> (JoinOnDrop<()>, Sender) { @@ -210,7 +226,12 @@ pub(crate) fn spawn_tip_thread( let _timer = request_timer(&metrics, "tip_to_checkpoint_reset_tip_to"); tip_handler - .reset_tip_to(&state_layout, &cp, Some(&mut thread_pool)) + .reset_tip_to( + &state_layout, + &cp, + lsmt_storage, + Some(&mut thread_pool), + ) .unwrap_or_else(|err| { fatal!( log, @@ -220,7 +241,6 @@ pub(crate) fn spawn_tip_thread( ); }); } - TipRequest::FlushPageMapDelta { height, pagemaps } => { let _timer = request_timer(&metrics, "flush_unflushed_delta"); #[cfg(debug_assertions)] @@ -240,26 +260,41 @@ pub(crate) fn spawn_tip_thread( ( truncate, page_map, - page_map_path( - &log, + page_map_paths( &mut tip_handler, height, &page_map_type, - ), + ) + .unwrap_or_else(|err| { + fatal!(log, "Failed to flush page map: {}", err); + }), ) }, ), - |(truncate, page_map, path)| { + |( + truncate, + page_map, + PageMapPaths { + base_file_path, + existing_overlays, + next_overlay_path, + }, + )| { if *truncate { - truncate_path(&log, path); + truncate_pagemap(&log, base_file_path, existing_overlays); } if page_map.is_some() && !page_map.as_ref().unwrap().unflushed_delta_is_empty() { + let dst = PersistDestination::new( + base_file_path.clone(), + next_overlay_path.clone(), + lsmt_storage, + ); page_map .as_ref() .unwrap() - .persist_unflushed_delta(path) + .persist_unflushed_delta(dst) .unwrap_or_else(|err| { fatal!( log, @@ -294,6 +329,7 @@ pub(crate) fn spawn_tip_thread( ); }), &mut thread_pool, + lsmt_storage, ) .unwrap_or_else(|err| { fatal!(log, "Failed to serialize to tip @{}: {}", height, err); @@ -306,6 +342,7 @@ pub(crate) fn spawn_tip_thread( .reset_tip_to( &state_layout, &checkpoint_layout, + lsmt_storage, Some(&mut thread_pool), ) .unwrap_or_else(|err| { @@ -377,6 +414,7 @@ fn serialize_to_tip( state: &ReplicatedState, tip: &CheckpointLayout>, thread_pool: &mut scoped_threadpool::Pool, + lsmt_storage: FlagStatus, ) -> Result<(), CheckpointError> { // Serialize ingress history separately. The `SystemMetadata` proto does not // encode it. @@ -412,7 +450,7 @@ fn serialize_to_tip( })?; let results = parallel_map(thread_pool, state.canisters_iter(), |canister_state| { - serialize_canister_to_tip(log, canister_state, tip) + serialize_canister_to_tip(log, canister_state, tip, lsmt_storage) }); for result in results.into_iter() { @@ -426,6 +464,7 @@ fn serialize_canister_to_tip( log: &ReplicaLogger, canister_state: &CanisterState, tip: &CheckpointLayout>, + lsmt_storage: FlagStatus, ) -> Result<(), CheckpointError> { let canister_layout = tip.canister(&canister_state.canister_id())?; canister_layout @@ -457,14 +496,24 @@ fn serialize_canister_to_tip( .serialize(&execution_state.wasm_binary.binary)?; } } + let memory_dst = PersistDestination::new( + canister_layout.vmemory_0(), + canister_layout.vmemory_0_overlay(tip.height()), + lsmt_storage, + ); + let stable_dst = PersistDestination::new( + canister_layout.stable_memory_blob(), + canister_layout.stable_memory_overlay(tip.height()), + lsmt_storage, + ); execution_state .wasm_memory .page_map - .persist_delta(&canister_layout.vmemory_0())?; + .persist_delta(memory_dst)?; execution_state .stable_memory .page_map - .persist_delta(&canister_layout.stable_memory_blob())?; + .persist_delta(stable_dst)?; Some(ExecutionStateBits { exported_globals: execution_state.exported_globals.clone(), @@ -477,18 +526,31 @@ fn serialize_canister_to_tip( }) } None => { - truncate_path(log, &canister_layout.vmemory_0()); - truncate_path(log, &canister_layout.stable_memory_blob()); + truncate_pagemap( + log, + &canister_layout.vmemory_0(), + &canister_layout.vmemory_0_overlays()?, + ); + truncate_pagemap( + log, + &canister_layout.stable_memory_blob(), + &canister_layout.stable_memory_overlays()?, + ); canister_layout.wasm().try_delete_file()?; None } }; + let wasm_chunk_store_dst = PersistDestination::new( + canister_layout.wasm_chunk_store(), + canister_layout.wasm_chunk_store_overlay(tip.height()), + lsmt_storage, + ); canister_state .system_state .wasm_chunk_store .page_map() - .persist_delta(&canister_layout.wasm_chunk_store())?; + .persist_delta(wasm_chunk_store_dst)?; // Priority credit must be zero at this point assert_eq!(canister_state.scheduler_state.priority_credit.get(), 0); @@ -629,7 +691,7 @@ pub fn defrag_tip( Ok(()) } -fn truncate_path(log: &ReplicaLogger, path: &Path) { +fn truncate_pagemap(log: &ReplicaLogger, path: &Path, overlays: &[PathBuf]) { if let Err(err) = nix::unistd::truncate(path, 0) { // It's OK if the file doesn't exist, everything else is a fatal error. if err != nix::errno::Errno::ENOENT { @@ -641,6 +703,17 @@ fn truncate_path(log: &ReplicaLogger, path: &Path) { ) } } + + for overlay in overlays { + std::fs::remove_file(overlay).unwrap_or_else(|err| { + fatal!( + log, + "Failed to remove overlay file {}: {}", + overlay.display(), + err + ); + }); + } } #[allow(clippy::too_many_arguments)] @@ -802,6 +875,7 @@ fn handle_compute_manifest_request( #[cfg(test)] mod test { use super::*; + use ic_config::state_manager::lsmt_storage_default; use ic_metrics::MetricsRegistry; use ic_test_utilities::types::ids::canister_test_id; use ic_test_utilities_logger::with_test_replica_logger; @@ -816,8 +890,14 @@ mod test { let metrics_registry = ic_metrics::MetricsRegistry::new(); let metrics = StateManagerMetrics::new(&metrics_registry); let tip_handler = layout.capture_tip_handler(); - let (_h, _s) = - spawn_tip_thread(log, tip_handler, layout, metrics, MaliciousFlags::default()); + let (_h, _s) = spawn_tip_thread( + log, + tip_handler, + layout, + lsmt_storage_default(), + metrics, + MaliciousFlags::default(), + ); }); }