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

feat: add chunk application stats #12797

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
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
10 changes: 10 additions & 0 deletions chain/chain/src/chain_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ impl<'a> ChainUpdate<'a> {
apply_result.contract_updates,
);
}
self.chain_store_update.save_chunk_apply_stats(
*block_hash,
shard_id,
apply_result.stats,
);
}
ShardUpdateResult::OldChunk(OldChunkResult { shard_uid, apply_result }) => {
// The chunk is missing but some fields may need to be updated
Expand Down Expand Up @@ -177,6 +182,11 @@ impl<'a> ChainUpdate<'a> {
apply_result.contract_updates,
);
}
self.chain_store_update.save_chunk_apply_stats(
*block_hash,
shard_uid.shard_id(),
apply_result.stats,
);
Copy link
Contributor Author

@jancionear jancionear Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saving chunk stats here means that only chunks applied inside blocks will have their stats saved. Stateless chunk validators will not save any stats. In the future we could change it to save it somewhere else, but it's good enough for the first version.

}
};
Ok(())
Expand Down
4 changes: 4 additions & 0 deletions chain/chain/src/garbage_collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@ impl<'a> ChainStoreUpdate<'a> {
self.gc_outgoing_receipts(&block_hash, shard_id);
self.gc_col(DBCol::IncomingReceipts, &block_shard_id);
self.gc_col(DBCol::StateTransitionData, &block_shard_id);
self.gc_col(DBCol::ChunkApplyStats, &block_shard_id);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could use some other garbage collection logic to keep the stats for longer than three epochs. Maybe something similar to LatestWitnesses where the last N witnesses are kept in the database? It's annoying that useful data like these stats disappears after three epochs, especially in tests which have to run for a few epochs. Can be changed later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that it would be cool to keep those longer and agreed to keep the first version simple.


// For incoming State Parts it's done in chain.clear_downloaded_parts()
// The following code is mostly for outgoing State Parts.
Expand Down Expand Up @@ -1017,6 +1018,9 @@ impl<'a> ChainStoreUpdate<'a> {
DBCol::StateSyncNewChunks => {
store_update.delete(col, key);
}
DBCol::ChunkApplyStats => {
store_update.delete(col, key);
}
DBCol::DbVersion
| DBCol::BlockMisc
| DBCol::_GCCount
Expand Down
6 changes: 4 additions & 2 deletions chain/chain/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,9 +355,10 @@ impl NightshadeRuntime {

let total_balance_burnt = apply_result
.stats
.balance
.tx_burnt_amount
.checked_add(apply_result.stats.other_burnt_amount)
.and_then(|result| result.checked_add(apply_result.stats.slashed_burnt_amount))
.checked_add(apply_result.stats.balance.other_burnt_amount)
.and_then(|result| result.checked_add(apply_result.stats.balance.slashed_burnt_amount))
.ok_or_else(|| {
Error::Other("Integer overflow during burnt balance summation".to_string())
})?;
Expand Down Expand Up @@ -387,6 +388,7 @@ impl NightshadeRuntime {
bandwidth_requests: apply_result.bandwidth_requests,
bandwidth_scheduler_state_hash: apply_result.bandwidth_scheduler_state_hash,
contract_updates: apply_result.contract_updates,
stats: apply_result.stats,
};

Ok(result)
Expand Down
44 changes: 44 additions & 0 deletions chain/chain/src/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use near_chain_primitives::error::Error;
use near_epoch_manager::EpochManagerAdapter;
use near_primitives::block::Tip;
use near_primitives::checked_feature;
use near_primitives::chunk_apply_stats::{ChunkApplyStats, ChunkApplyStatsV0};
use near_primitives::errors::InvalidTxError;
use near_primitives::hash::CryptoHash;
use near_primitives::merkle::{MerklePath, PartialMerkleTree};
Expand Down Expand Up @@ -112,6 +113,11 @@ pub trait ChainStoreAccess {
block_hash: &CryptoHash,
shard_uid: &ShardUId,
) -> Result<Arc<ChunkExtra>, Error>;
fn get_chunk_apply_stats(
&self,
block_hash: &CryptoHash,
shard_id: &ShardId,
) -> Result<Option<ChunkApplyStats>, Error>;
/// Get block header.
fn get_block_header(&self, h: &CryptoHash) -> Result<BlockHeader, Error>;
/// Returns hash of the block on the main chain for given height.
Expand Down Expand Up @@ -941,6 +947,14 @@ impl ChainStoreAccess for ChainStore {
ChainStoreAdapter::get_chunk_extra(self, block_hash, shard_uid)
}

fn get_chunk_apply_stats(
&self,
block_hash: &CryptoHash,
shard_id: &ShardId,
) -> Result<Option<ChunkApplyStats>, Error> {
ChainStoreAdapter::get_chunk_apply_stats(&self, block_hash, shard_id)
}

/// Get block header.
fn get_block_header(&self, h: &CryptoHash) -> Result<BlockHeader, Error> {
ChainStoreAdapter::get_block_header(self, h)
Expand Down Expand Up @@ -1084,6 +1098,7 @@ pub struct ChainStoreUpdate<'a> {
add_state_sync_infos: Vec<StateSyncInfo>,
remove_state_sync_infos: Vec<CryptoHash>,
challenged_blocks: HashSet<CryptoHash>,
chunk_apply_stats: HashMap<(CryptoHash, ShardId), ChunkApplyStats>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small suggestion: shard uid is the better unique identifier of a shard. that being said it's often not readily available, in that case don't worry about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIU from now on the plan is to add new shard ids instead of increasing UId versions, so it should be unique enough. ShardId is more user friendly so I went with that.

}

impl<'a> ChainStoreUpdate<'a> {
Expand All @@ -1107,6 +1122,7 @@ impl<'a> ChainStoreUpdate<'a> {
add_state_sync_infos: vec![],
remove_state_sync_infos: vec![],
challenged_blocks: HashSet::default(),
chunk_apply_stats: HashMap::default(),
}
}
}
Expand Down Expand Up @@ -1234,6 +1250,18 @@ impl<'a> ChainStoreAccess for ChainStoreUpdate<'a> {
}
}

fn get_chunk_apply_stats(
&self,
block_hash: &CryptoHash,
shard_id: &ShardId,
) -> Result<Option<ChunkApplyStats>, Error> {
if let Some(stats) = self.chunk_apply_stats.get(&(*block_hash, *shard_id)) {
Ok(Some(stats.clone()))
} else {
self.chain_store.get_chunk_apply_stats(block_hash, shard_id)
}
}

/// Get block header.
fn get_block_header(&self, hash: &CryptoHash) -> Result<BlockHeader, Error> {
if let Some(header) = self.chain_store_cache_update.headers.get(hash).cloned() {
Expand Down Expand Up @@ -1746,6 +1774,15 @@ impl<'a> ChainStoreUpdate<'a> {
self.chain_store_cache_update.processed_block_heights.insert(height);
}

pub fn save_chunk_apply_stats(
&mut self,
block_hash: CryptoHash,
shard_id: ShardId,
stats: ChunkApplyStatsV0,
) {
self.chunk_apply_stats.insert((block_hash, shard_id), ChunkApplyStats::V0(stats));
}

pub fn inc_block_refcount(&mut self, block_hash: &CryptoHash) -> Result<(), Error> {
let refcount = match self.get_block_refcount(block_hash) {
Ok(refcount) => refcount,
Expand Down Expand Up @@ -2159,6 +2196,13 @@ impl<'a> ChainStoreUpdate<'a> {
&(),
)?;
}
for ((block_hash, shard_id), stats) in self.chunk_apply_stats.iter() {
store_update.set_ser(
DBCol::ChunkApplyStats,
&get_block_shard_id(block_hash, *shard_id),
stats,
)?;
}
for other in self.store_updates.drain(..) {
store_update.merge(other);
}
Expand Down
2 changes: 2 additions & 0 deletions chain/chain/src/test_utils/kv_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use near_primitives::account::{AccessKey, Account};
use near_primitives::apply::ApplyChunkReason;
use near_primitives::bandwidth_scheduler::BandwidthRequests;
use near_primitives::block::Tip;
use near_primitives::chunk_apply_stats::ChunkApplyStatsV0;
use near_primitives::congestion_info::{CongestionInfo, ExtendedCongestionInfo};
use near_primitives::epoch_block_info::BlockInfo;
use near_primitives::epoch_info::EpochInfo;
Expand Down Expand Up @@ -1187,6 +1188,7 @@ impl RuntimeAdapter for KeyValueRuntime {
bandwidth_requests: BandwidthRequests::default_for_protocol_version(PROTOCOL_VERSION),
bandwidth_scheduler_state_hash: CryptoHash::default(),
contract_updates: Default::default(),
stats: ChunkApplyStatsV0::dummy(),
})
}

Expand Down
2 changes: 2 additions & 0 deletions chain/chain/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use near_primitives::bandwidth_scheduler::BlockBandwidthRequests;
pub use near_primitives::block::{Block, BlockHeader, Tip};
use near_primitives::challenge::ChallengesResult;
use near_primitives::checked_feature;
use near_primitives::chunk_apply_stats::ChunkApplyStatsV0;
use near_primitives::congestion_info::BlockCongestionInfo;
use near_primitives::congestion_info::CongestionInfo;
use near_primitives::congestion_info::ExtendedCongestionInfo;
Expand Down Expand Up @@ -115,6 +116,7 @@ pub struct ApplyChunkResult {
pub bandwidth_scheduler_state_hash: CryptoHash,
/// Contracts accessed and deployed while applying the chunk.
pub contract_updates: ContractUpdates,
pub stats: ChunkApplyStatsV0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the versioned struct instead of the enum?

nit: please add a comment

}

impl ApplyChunkResult {
Expand Down
2 changes: 1 addition & 1 deletion core/primitives/src/bandwidth_scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ pub struct LinkAllowance {
}

/// Parameters used in the bandwidth scheduler algorithm.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct BandwidthSchedulerParams {
/// This much bandwidth is granted by default.
/// base_bandwidth = (max_shard_bandwidth - max_single_grant) / (num_shards - 1)
Expand Down
Loading
Loading