Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up storage constants and remove 32GB limitation #2289

Merged
merged 1 commit into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/internet_identity/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::activity_stats::ActivityStats;
use crate::archive::{ArchiveData, ArchiveState, ArchiveStatusCache};
use crate::state::temp_keys::TempKeys;
use crate::storage::anchor::Anchor;
use crate::storage::DEFAULT_RANGE_SIZE;
use crate::storage::MAX_ENTRIES;
use crate::{random_salt, Storage};
use asset_util::CertifiedAssets;
use candid::{CandidType, Deserialize};
Expand Down Expand Up @@ -212,7 +212,7 @@ pub fn init_new() {
let storage = Storage::new(
(
FIRST_ANCHOR_NUMBER,
FIRST_ANCHOR_NUMBER.saturating_add(DEFAULT_RANGE_SIZE),
FIRST_ANCHOR_NUMBER.saturating_add(MAX_ENTRIES),
),
memory,
);
Expand Down
40 changes: 13 additions & 27 deletions src/internet_identity/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,19 +119,10 @@ mod tests;
/// * version 8: same as 7, but archive entries buffer in stable memory
const SUPPORTED_LAYOUT_VERSIONS: RangeInclusive<u8> = 7..=8;

/// Reserved space for the header before the anchor records start.
const ENTRY_OFFSET: u64 = 2 * WASM_PAGE_SIZE_IN_BYTES as u64; // 1 page reserved for II config, 1 for memory manager
const DEFAULT_ENTRY_SIZE: u16 = 4096;
const EMPTY_SALT: [u8; 32] = [0; 32];
const GB: u64 = 1 << 30;

const MAX_STABLE_MEMORY_SIZE: u64 = 32 * GB;
const MAX_WASM_PAGES: u64 = MAX_STABLE_MEMORY_SIZE / WASM_PAGE_SIZE_IN_BYTES as u64;

/// In practice, II has 48 GB of stable memory available.
/// This limit has last been raised when it was still 32 GB.
const STABLE_MEMORY_SIZE: u64 = 32 * GB;

const PERSISTENT_STATE_MAGIC: [u8; 4] = *b"IIPS"; // II Persistent State

/// MemoryManager parameters.
Expand All @@ -144,9 +135,13 @@ const ARCHIVE_BUFFER_MEMORY_ID: MemoryId = MemoryId::new(ARCHIVE_BUFFER_MEMORY_I
// This value results in 256 GB of total managed memory, which should be enough
// for the foreseeable future.
const BUCKET_SIZE_IN_PAGES: u16 = 128;
const MAX_MANAGED_MEMORY_SIZE: u64 = 256 * GB;
const MAX_MANAGED_WASM_PAGES: u64 = MAX_MANAGED_MEMORY_SIZE / WASM_PAGE_SIZE_IN_BYTES as u64;

/// The maximum number of anchors this canister can store.
pub const DEFAULT_RANGE_SIZE: u64 = (STABLE_MEMORY_SIZE - ENTRY_OFFSET) / DEFAULT_ENTRY_SIZE as u64;
pub const MAX_ENTRIES: u64 = (MAX_MANAGED_WASM_PAGES - BUCKET_SIZE_IN_PAGES as u64) // deduct one bucket for the archive entries buffer
* WASM_PAGE_SIZE_IN_BYTES as u64
/ DEFAULT_ENTRY_SIZE as u64;

pub type Salt = [u8; 32];

Expand Down Expand Up @@ -189,7 +184,6 @@ struct Header {
id_range_hi: u64,
entry_size: u16,
salt: [u8; 32],
first_entry_offset: u64,
}

impl<M: Memory + Clone> Storage<M> {
Expand All @@ -202,14 +196,14 @@ impl<M: Memory + Clone> Storage<M> {
));
}

if (id_range_hi - id_range_lo) > DEFAULT_RANGE_SIZE {
if (id_range_hi - id_range_lo) > MAX_ENTRIES {
trap(&format!(
"id range [{id_range_lo}, {id_range_hi}) is too large for a single canister (max {DEFAULT_RANGE_SIZE} entries)",
"id range [{id_range_lo}, {id_range_hi}) is too large for a single canister (max {MAX_ENTRIES} entries)",
));
}
let header_memory = RestrictedMemory::new(memory.clone(), 0..1);
let memory_manager = MemoryManager::init_with_bucket_size(
RestrictedMemory::new(memory, 1..MAX_WASM_PAGES),
RestrictedMemory::new(memory, 1..MAX_MANAGED_WASM_PAGES),
BUCKET_SIZE_IN_PAGES,
);
let anchor_memory = memory_manager.get(ANCHOR_MEMORY_ID);
Expand All @@ -224,7 +218,6 @@ impl<M: Memory + Clone> Storage<M> {
id_range_hi,
entry_size: DEFAULT_ENTRY_SIZE,
salt: EMPTY_SALT,
first_entry_offset: ENTRY_OFFSET,
},
header_memory,
anchor_memory,
Expand Down Expand Up @@ -292,7 +285,7 @@ impl<M: Memory + Clone> Storage<M> {
match header.version {
7 | 8 => {
let header_memory = RestrictedMemory::new(memory.clone(), 0..1);
let managed_memory = RestrictedMemory::new(memory, 1..MAX_WASM_PAGES);
let managed_memory = RestrictedMemory::new(memory, 1..MAX_MANAGED_WASM_PAGES);
let memory_manager =
MemoryManager::init_with_bucket_size(managed_memory, BUCKET_SIZE_IN_PAGES);
let anchor_memory = memory_manager.get(ANCHOR_MEMORY_ID);
Expand Down Expand Up @@ -373,12 +366,6 @@ impl<M: Memory + Clone> Storage<M> {
self.header.num_anchors as usize
}

/// Returns the maximum number of entries that this storage can fit.
pub fn max_entries(&self) -> usize {
((STABLE_MEMORY_SIZE - self.header.first_entry_offset) / self.header.entry_size as u64)
as usize
}

pub fn assigned_anchor_number_range(&self) -> (AnchorNumber, AnchorNumber) {
(self.header.id_range_lo, self.header.id_range_hi)
}
Expand All @@ -389,11 +376,10 @@ impl<M: Memory + Clone> Storage<M> {
"set_anchor_number_range: improper Identity Anchor range [{lo}, {hi})"
));
}
let max_entries = self.max_entries() as u64;
if (hi - lo) > max_entries {
if (hi - lo) > MAX_ENTRIES {
trap(&format!(
"set_anchor_number_range: specified range [{lo}, {hi}) is too large for this canister \
(max {max_entries} entries)"
(max {MAX_ENTRIES} entries)"
));
}

Expand All @@ -403,15 +389,15 @@ impl<M: Memory + Clone> Storage<M> {
trap(&format!(
"set_anchor_number_range: specified range [{lo}, {hi}) does not start from the same number ({}) \
as the existing range thus would make existing anchors invalid"
, {self.header.id_range_lo}));
, { self.header.id_range_lo }));
}
// Check that all _existing_ anchors fit into the new range. I.e. making the range smaller
// is ok as long as the range reduction only affects _unused_ anchor number.
if (hi - lo) < self.header.num_anchors as u64 {
trap(&format!(
"set_anchor_number_range: specified range [{lo}, {hi}) does not accommodate all {} anchors \
thus would make existing anchors invalid"
, {self.header.num_anchors}));
, { self.header.num_anchors }));
}
}

Expand Down
15 changes: 8 additions & 7 deletions src/internet_identity/src/storage/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::activity_stats::{ActivityStats, CompletedActivityStats, OngoingActivi
use crate::archive::{ArchiveData, ArchiveState};
use crate::state::PersistentState;
use crate::storage::anchor::{Anchor, Device};
use crate::storage::{Header, PersistentStateError, StorageError};
use crate::storage::{Header, PersistentStateError, StorageError, MAX_ENTRIES};
use crate::Storage;
use candid::Principal;
use ic_stable_structures::{Memory, VectorMemory};
Expand All @@ -13,7 +13,7 @@ use internet_identity_interface::internet_identity::types::{
use serde_bytes::ByteBuf;
use std::rc::Rc;

const HEADER_SIZE: usize = 66;
const HEADER_SIZE: usize = 58;

#[test]
fn should_match_actual_header_size() {
Expand All @@ -22,10 +22,11 @@ fn should_match_actual_header_size() {
}

#[test]
fn should_report_max_number_of_entries_for_32gb() {
let memory = VectorMemory::default();
let storage = Storage::new((1, 2), memory);
assert_eq!(storage.max_entries(), 8_388_576);
fn should_report_max_number_of_entries_for_256gb() {
// The maximum number of entries that could be supported by the canister without making any changes
// is constant. This test now exists to make sure any dev is aware of the limit if making changes
// to the underlying constants.
assert_eq!(MAX_ENTRIES, 67_106_816);
}

#[test]
Expand All @@ -38,7 +39,7 @@ fn should_serialize_header_v8() {
assert_eq!(storage.version(), 8);
let mut buf = vec![0; HEADER_SIZE];
memory.read(0, &mut buf);
assert_eq!(buf, hex::decode("494943080000000001000000000000000200000000000000001005050505050505050505050505050505050505050505050505050505050505050000020000000000").unwrap());
assert_eq!(buf, hex::decode("49494308000000000100000000000000020000000000000000100505050505050505050505050505050505050505050505050505050505050505").unwrap());
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion src/internet_identity/tests/integration/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ fn metrics_should_list_default_user_range() -> Result<(), CallError> {
let (min_user_number, _) = parse_metric(&metrics, "internet_identity_min_user_number");
let (max_user_number, _) = parse_metric(&metrics, "internet_identity_max_user_number");
assert_eq!(min_user_number, 10_000f64);
assert_eq!(max_user_number, 8_398_575f64);
assert_eq!(max_user_number, 67_116_815f64);
Ok(())
}

Expand Down
Loading