-
Notifications
You must be signed in to change notification settings - Fork 4.5k
calculate_capitalization uses hash calculation #17443
Conversation
710b10c
to
9d17bb3
Compare
@sakridge replacing an index scan with a storage scan will not work if there is account data in the write cache. Here's where we get where we have cached accounts loaded: solana/runtime/src/accounts.rs Lines 623 to 628 in 9a5330b
Here's the call that gets us to calculate_capitalization. solana/ledger/src/blockstore_processor.rs Line 558 in 179856c
We would presumably need to flush the cache before this call, or we detect there are items in the cache and we fallback to the account scan as before. Or, we do something more complicated. If we're just worried about the cap scan at startup, there will not be anything in the cache. We could just change that code path. |
c480e35
to
4dc62ad
Compare
79926ff
to
d99a6c6
Compare
This code path now supports appending the write cache data per slot to the store data per slot. |
runtime/src/accounts_db.rs
Outdated
let accounts = storage.accounts.accounts(0); | ||
accounts.into_iter().for_each(|stored_account| { | ||
scan_func(LoadedAccount::Stored(stored_account), &mut retval, slot) | ||
}); | ||
} | ||
} | ||
if let Some(cache) = accounts_cache { |
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.
@carllin - this is interesting to you. We are using the read cache to augment the data in stores in order to calculate capitalization on banks where we haven't flushed the cache yet.
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, this is because on startup when we run if !bank_forks.root_bank().calculate_and_verify_capitalization() {
from do_process_blockstore_from_root()
the cache may not have been flushed yet. This might be a good comment to add to the calculate_capitalization()
function for why we pass the use_cache
flag. If we do as this comment suggests https://github.com/solana-labs/solana/pull/17443/files#r645215712, then that might also be good self-documentation for that function
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 don't understand what you linked to. Do you mean can_cached_slot_be_unflushed
?
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.
arg! yeah sorry the links to the comments failed, good thing your intuition is good 😃. Yeah I meant this comment: #17443 (comment) regarding can_cached_slot_be_unflushed
.
Codecov Report
@@ Coverage Diff @@
## master #17443 +/- ##
=========================================
- Coverage 82.7% 82.6% -0.2%
=========================================
Files 431 431
Lines 120590 121055 +465
=========================================
+ Hits 99839 100025 +186
- Misses 20751 21030 +279 |
d0e0365
to
b237b0e
Compare
runtime/src/accounts.rs
Outdated
.accounts_db | ||
.update_accounts_hash_with_index_option(false, false, slot, ancestors, None, true) | ||
.1; | ||
// debug code for the moment |
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.
this old code path remains for now to make sure we are getting the right results in all cases. I'll run this against mnb for a while. I am not yet familiar with all the ways calculate_capitalization is called. I'll look into it.
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.
so, to be clear I will not complete this pr with the old code path and the assert present.
runtime/src/accounts.rs
Outdated
let cap = self | ||
.accounts_db | ||
.update_accounts_hash_with_index_option(false, false, slot, ancestors, None, true) | ||
.1; |
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.
we could also pass a parameter here to prevent actually calculating a hash value since we throw it away.
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.
Also, I need to change this to call a level deeper and not update the hash - but just calculate the hash to maintain the behavior of the function I'm attempting to replace.
runtime/src/sorted_storages.rs
Outdated
} | ||
|
||
// source[i] is in slot slots[i] | ||
// assumptions: | ||
// 1. slots vector contains unique slot #s. | ||
// 2. slots and source are the same len | ||
pub fn new_with_slots(source: &'a [SnapshotStorage], slots: &[Slot]) -> Self { | ||
pub fn new_with_slots( |
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.
nit: maybe rename to new_from_storage_entries
?
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.
some callers have only &[SnapshotStorage]. We have to figure out the corresponding slots. Restoring from a snapshot is such a code path. Those callers call 'new'. Tests are another code path.
Other callers can easily generate a corresponding Slot per storage. Those callers call 'new_with_slots'.
We can rename either of the 'new' functions. This is the brief history of the naming.
7b6e27f
to
0360366
Compare
runtime/src/sorted_storages.rs
Outdated
let mut min = Slot::MAX; | ||
let mut max = Slot::MIN; | ||
// none, either, or both of min/max could be specified | ||
if let Some(slot) = min_slot { | ||
min = std::cmp::min(slot, min); | ||
max = std::cmp::max(slot + 1, max); | ||
} | ||
if let Some(slot) = max_slot_inclusive { | ||
min = std::cmp::min(slot, min); | ||
max = std::cmp::max(slot + 1, max); | ||
} | ||
|
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.
Laid out explicitly like so, this block is easier for me to reason about all the cases:
let (mut min, mut max) = match (min_slot, max_slot_inclusive) {
(Some(min_slot), Some(max_slot_inclusive)) => {
assert!(max_slot_inclusive >= min_slot);
(min_slot, max_slot_inclusive)
},
(Some(min_slot), None) => (min_slot, min_slot + 1),
(None, Some(max_slot_inclusive)) => (max_slot_inclusive, max_slot_inclusive + 1),
(None, None) => (Slot::MAX, Slot::MIN),
};
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'd prefer not to add an assert. Also, it seems possible that calculations of min could end up > max given that we have several sources we're merging: global roots, ancestors, current slot of bank?, and the global write cache. We are really looking for the max range that we have to iterate over to check stores or write cache for accounts that we should include in our calculation.
I did rework the function to make it much more clear what is going on. min_slot and max_slot_inclusive are just 2 more 'slot' values run through the min/max calculation, along with all the slots in the iter.
let min_root = self.accounts_index.min_root(); | ||
let storages = SortedStorages::new_with_slots( | ||
combined_maps.iter().zip(slots.iter()), | ||
min_root, |
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.
One thing to note is there can potentially be a large gap between the min root and the first slot in the cache (we keep around 425,000 roots, but we only keep around the latest 10 seconds of roots at startup in the cache).
In fact, it may actually be faster to just iterate through the cache to find the smallest slot, than to iterate over the 400k slots starting from the min root. If we ever need to repeatedly call this function, might be more efficient to either:
- Have a separate min for the cache
- Cache the starting point for the cache search after performing it the first time.
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.
A few nits, but otherwise lgtm!
* calculate_capitalization uses hash calculation * feedback * remove debugging code, clean up slot math (cherry picked from commit d4cc975) # Conflicts: # runtime/src/accounts_db.rs # runtime/src/sorted_storages.rs
* calculate_capitalization uses hash calculation * feedback * remove debugging code, clean up slot math
Problem
#17095
Summary of Changes
rework calculate_capitalization to use the store scan instead of index scan. Call update_accounts_hash_with_index_option with index=false.
Fixes #