-
Notifications
You must be signed in to change notification settings - Fork 99
feat: prune InnerForest
#1635
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
base: main
Are you sure you want to change the base?
feat: prune InnerForest
#1635
Changes from all commits
4d0c39b
0c96836
edeb505
74efb5a
b201602
e2c5289
a938411
68b4b04
0743c37
1c9bb4f
774c2fd
63d4deb
65bc39d
843dcbe
cb18729
b9f2a45
cb6cf7e
899fccb
3ade4d6
afcf072
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
drahnr marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,13 @@ | ||
| use std::collections::{BTreeMap, BTreeSet, HashSet}; | ||
| use std::mem::size_of; | ||
| use std::ops::RangeInclusive; | ||
| use std::path::PathBuf; | ||
|
|
||
| use anyhow::Context; | ||
| use diesel::{Connection, QueryableByName, RunQueryDsl, SqliteConnection}; | ||
| use miden_node_proto::domain::account::{AccountInfo, AccountSummary}; | ||
| use miden_node_proto::generated as proto; | ||
| use miden_node_utils::limiter::MAX_RESPONSE_PAYLOAD_BYTES; | ||
| use miden_node_utils::tracing::OpenTelemetrySpanExt; | ||
| use miden_protocol::Word; | ||
| use miden_protocol::account::{AccountHeader, AccountId, AccountStorageHeader}; | ||
|
|
@@ -34,6 +36,12 @@ use crate::db::models::{Page, queries}; | |
| use crate::errors::{DatabaseError, DatabaseSetupError, NoteSyncError, StateSyncError}; | ||
| use crate::genesis::GenesisBlock; | ||
|
|
||
| const ROW_OVERHEAD_BYTES: usize = 2 * size_of::<Word>() + size_of::<u32>() + size_of::<u8>(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overhead of what? |
||
|
|
||
| fn default_storage_map_entries_limit() -> usize { | ||
| MAX_RESPONSE_PAYLOAD_BYTES / ROW_OVERHEAD_BYTES | ||
| } | ||
|
|
||
| pub(crate) mod manager; | ||
|
|
||
| mod migrations; | ||
|
|
@@ -600,13 +608,106 @@ impl Db { | |
| &self, | ||
| account_id: AccountId, | ||
| block_range: RangeInclusive<BlockNumber>, | ||
| entries_limit: Option<usize>, | ||
| ) -> Result<StorageMapValuesPage> { | ||
| let entries_limit = entries_limit.unwrap_or_else(default_storage_map_entries_limit); | ||
|
|
||
| self.transact("select storage map sync values", move |conn| { | ||
| models::queries::select_account_storage_map_values(conn, account_id, block_range) | ||
| models::queries::select_account_storage_map_values_paged( | ||
| conn, | ||
| account_id, | ||
| block_range, | ||
| entries_limit, | ||
| ) | ||
| }) | ||
| .await | ||
| } | ||
|
|
||
| /// Reconstructs storage map details from the database for a specific slot at a block. | ||
| /// | ||
| /// Used as fallback when `InnerForest` cache misses (historical or evicted queries). | ||
| /// Rebuilds all entries by querying the DB and filtering to the specific slot. | ||
| /// | ||
| /// Returns: | ||
| /// - `::LimitExceeded` when too many entries are present | ||
| /// - `::AllEntries` if the size is less than or equal given `entries_limit`, if any | ||
| pub(crate) async fn reconstruct_storage_map_from_db( | ||
| &self, | ||
| account_id: AccountId, | ||
| slot_name: miden_protocol::account::StorageSlotName, | ||
| block_num: BlockNumber, | ||
| entries_limit: Option<usize>, | ||
| ) -> Result<miden_node_proto::domain::account::AccountStorageMapDetails> { | ||
| use miden_node_proto::domain::account::{AccountStorageMapDetails, StorageMapEntries}; | ||
| use miden_protocol::EMPTY_WORD; | ||
|
|
||
| // TODO this remains expensive with a large history until we implement pruning for DB | ||
| // columns | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's already done in |
||
| let mut values = Vec::new(); | ||
| let mut block_range_start = BlockNumber::GENESIS; | ||
| let entries_limit = entries_limit.unwrap_or_else(default_storage_map_entries_limit); | ||
|
|
||
| let mut page = self | ||
| .select_storage_map_sync_values( | ||
| account_id, | ||
| block_range_start..=block_num, | ||
| Some(entries_limit), | ||
| ) | ||
| .await?; | ||
|
|
||
| values.extend(page.values); | ||
| let mut last_block_included = page.last_block_included; | ||
|
|
||
| loop { | ||
| if page.last_block_included == block_num || page.last_block_included < block_range_start | ||
| { | ||
| break; | ||
| } | ||
|
|
||
| block_range_start = page.last_block_included.child(); | ||
| page = self | ||
| .select_storage_map_sync_values( | ||
| account_id, | ||
| block_range_start..=block_num, | ||
| Some(entries_limit), | ||
| ) | ||
| .await?; | ||
|
|
||
| if page.last_block_included <= last_block_included { | ||
| return Ok(AccountStorageMapDetails::limit_exceeded(slot_name)); | ||
| } | ||
|
|
||
| last_block_included = page.last_block_included; | ||
| values.extend(page.values); | ||
| } | ||
drahnr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if page.last_block_included != block_num { | ||
| return Ok(AccountStorageMapDetails::limit_exceeded(slot_name)); | ||
| } | ||
|
|
||
| // Filter to the specific slot and collect latest values per key | ||
| let mut latest_values = BTreeMap::<Word, Word>::new(); | ||
| for value in values { | ||
| if value.slot_name == slot_name { | ||
| let raw_key = value.key; | ||
| latest_values.insert(raw_key, value.value); | ||
| } | ||
| } | ||
|
|
||
| // Remove EMPTY_WORD entries (deletions) | ||
| latest_values.retain(|_, v| *v != EMPTY_WORD); | ||
|
|
||
| if latest_values.len() > AccountStorageMapDetails::MAX_RETURN_ENTRIES { | ||
| return Ok(AccountStorageMapDetails::limit_exceeded(slot_name)); | ||
| } | ||
|
|
||
| let entries = Vec::from_iter(latest_values.into_iter()); | ||
| Ok(AccountStorageMapDetails { | ||
| slot_name, | ||
| entries: StorageMapEntries::AllEntries(entries), | ||
| }) | ||
| } | ||
|
|
||
| /// Emits size metrics for each table in the database, and the entire database. | ||
| #[instrument(target = COMPONENT, skip_all, err)] | ||
| pub async fn analyze_table_sizes(&self) -> Result<(), DatabaseError> { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.