-
Notifications
You must be signed in to change notification settings - Fork 7
feat: implement in-memory storage backend #183
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
feat: implement in-memory storage backend #183
Conversation
d7fe4bc to
56afdf7
Compare
955e4f1 to
7731223
Compare
3e311c7 to
5ef8999
Compare
7731223 to
a737cf2
Compare
5ef8999 to
84c813c
Compare
a737cf2 to
1eef699
Compare
84c813c to
9ca3987
Compare
1eef699 to
1c7a7a3
Compare
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension. |
1c7a7a3 to
f336fc3
Compare
| let mut collected_entries: Vec<(Nibbles, BranchNodeCompact)> = | ||
| if let Some(addr) = hashed_address { | ||
| // Storage trie cursor | ||
| for ((block, address, path), branch) in &storage.storage_branches { |
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.
Shouldn't we merge the changes from trie_updates for all blocks up to the specified max_block_number?
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.
I think this should merge them all already. The BTreeMap is sorted by block number ASC, so when we collect all the entries, it will choose the latest block <= max_block_number.
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.
If I understand correctly, we are storing live updates only in the trie_updates BTreeMap. We aren't saving data to storage_branches BTreeMap in the live syncing.
Hence live updates changes won't be visible to the cursor since cursor is only reading storage_branches BTreeMap
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.
ah I see, that should be saved to *_branches and hashed_*. The trie_updates and post_states are just indexes from block number to the slots/trie nodes changed for pruning/reorgs.
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.
fixed: fa65493
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.
Thanks. Will review it.
| .collect() | ||
| } else { | ||
| // Account trie cursor | ||
| for ((block, path), branch) in &storage.account_branches { |
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.
Same here
|
does this pr reference any issue? |
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.
Pull Request Overview
This PR implements an in-memory storage backend for external trie node storage and creates a comprehensive test suite that can work with any storage backend implementation. The changes enable easier testing and development of the external proofs feature while providing a foundation for adding future storage backends like SQLite.
Key changes:
- Implements a complete in-memory storage backend with thread-safe operations using tokio RwLock
- Creates an extensive test suite covering all storage operations, cursor functionality, and edge cases
- Adds proper documentation and comments to the storage trait interfaces
Reviewed Changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
crates/exex/external-proofs/src/in_memory.rs |
Complete in-memory implementation of ExternalStorage trait with cursors for accounts, storage, and trie branches |
crates/exex/external-proofs/src/storage_tests.rs |
Comprehensive test suite covering all storage operations, cursor navigation, block versioning, and edge cases |
crates/exex/external-proofs/src/storage.rs |
Added documentation comments to the storage trait and BlockStateDiff struct |
crates/exex/external-proofs/src/lib.rs |
Added module declarations for in_memory and storage_tests |
crates/exex/external-proofs/Cargo.toml |
Added tokio dependency and test dependencies with feature flags |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| #[cfg(test)] | ||
| mod tests { |
Copilot
AI
Oct 10, 2025
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.
The entire file is wrapped in a #[cfg(test)] module, but the file itself is already conditionally included only for tests via #[cfg(test)] in lib.rs. This creates redundant test-only compilation guards.
| let inner = self | ||
| .inner | ||
| .try_read() | ||
| .map_err(|_| ExternalStorageError::Other(eyre::eyre!("Failed to acquire read lock")))?; |
Copilot
AI
Oct 10, 2025
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.
Using try_read() in synchronous cursor methods could fail under contention. Consider using blocking read() or redesigning the API to be async, as lock contention in this context likely indicates a design issue rather than a recoverable error.
| let inner = self | ||
| .inner | ||
| .try_read() | ||
| .map_err(|_| ExternalStorageError::Other(eyre::eyre!("Failed to acquire read lock")))?; |
Copilot
AI
Oct 10, 2025
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.
Same lock contention issue as in trie_cursor. Using try_read() in synchronous cursor methods could fail under contention. Consider using blocking read() or redesigning the API to be async.
| let inner = self | ||
| .inner | ||
| .try_read() | ||
| .map_err(|_| ExternalStorageError::Other(eyre::eyre!("Failed to acquire read lock")))?; |
Copilot
AI
Oct 10, 2025
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.
Same lock contention issue as in previous cursor methods. Using try_read() in synchronous cursor methods could fail under contention. Consider using blocking read() or redesigning the API to be async.
| blocks_to_add: HashMap<u64, BlockStateDiff>, | ||
| ) -> ExternalStorageResult<()> { | ||
| let mut inner = self.inner.write().await; | ||
|
|
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.
Here as well, we need to update account_branches, storage_branches, hashed_accounts and hashed_storage.
| } | ||
|
|
||
| for (hashed_address, storage) in &block_state_diff.post_state.storages { | ||
| for (slot, value) in &storage.storage { |
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.
I think it's better to move the wiped logic here. Let me know what you think.
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.
good idea, moved this to a common fn: 65e441e
|
Clippy does not recognize the lint as being used even though it definitely is (removing it causes warnings). I had to use |
Based on #183 This PR adds a backfill job that accepts a DB transaction and copies the current state to the database. The transaction ensures we see a consistent view of the database at the current block, even if the node is syncing. This requires `--db.read-transaction-timeout 0`. This currently doesn't handle interrupting the job because the state may update while syncing and may read a different version of the database upon restart. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Ref #176 This PR adds an in-memory storage backend and tests that work with any storage backend. We can easily add more test cases to the file to test against a future SQLite backend. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Based on #183 This PR adds a backfill job that accepts a DB transaction and copies the current state to the database. The transaction ensures we see a consistent view of the database at the current block, even if the node is syncing. This requires `--db.read-transaction-timeout 0`. This currently doesn't handle interrupting the job because the state may update while syncing and may read a different version of the database upon restart. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Ref #176 This PR adds an in-memory storage backend and tests that work with any storage backend. We can easily add more test cases to the file to test against a future SQLite backend. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Based on #183 This PR adds a backfill job that accepts a DB transaction and copies the current state to the database. The transaction ensures we see a consistent view of the database at the current block, even if the node is syncing. This requires `--db.read-transaction-timeout 0`. This currently doesn't handle interrupting the job because the state may update while syncing and may read a different version of the database upon restart. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Ref #176 This PR adds an in-memory storage backend and tests that work with any storage backend. We can easily add more test cases to the file to test against a future SQLite backend. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Based on #183 This PR adds a backfill job that accepts a DB transaction and copies the current state to the database. The transaction ensures we see a consistent view of the database at the current block, even if the node is syncing. This requires `--db.read-transaction-timeout 0`. This currently doesn't handle interrupting the job because the state may update while syncing and may read a different version of the database upon restart. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Ref #176 This PR adds an in-memory storage backend and tests that work with any storage backend. We can easily add more test cases to the file to test against a future SQLite backend. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Based on #183 This PR adds a backfill job that accepts a DB transaction and copies the current state to the database. The transaction ensures we see a consistent view of the database at the current block, even if the node is syncing. This requires `--db.read-transaction-timeout 0`. This currently doesn't handle interrupting the job because the state may update while syncing and may read a different version of the database upon restart. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Ref #176 This PR adds an in-memory storage backend and tests that work with any storage backend. We can easily add more test cases to the file to test against a future SQLite backend. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Based on #183 This PR adds a backfill job that accepts a DB transaction and copies the current state to the database. The transaction ensures we see a consistent view of the database at the current block, even if the node is syncing. This requires `--db.read-transaction-timeout 0`. This currently doesn't handle interrupting the job because the state may update while syncing and may read a different version of the database upon restart. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Ref #176 This PR adds an in-memory storage backend and tests that work with any storage backend. We can easily add more test cases to the file to test against a future SQLite backend. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Based on #183 This PR adds a backfill job that accepts a DB transaction and copies the current state to the database. The transaction ensures we see a consistent view of the database at the current block, even if the node is syncing. This requires `--db.read-transaction-timeout 0`. This currently doesn't handle interrupting the job because the state may update while syncing and may read a different version of the database upon restart. --------- Co-authored-by: Arun Dhyani <[email protected]> Co-authored-by: Emilia Hane <[email protected]>
Ref #176
This PR adds an in-memory storage backend and tests that work with any storage backend. We can easily add more test cases to the file to test against a future SQLite backend.