Skip to content

Commit

Permalink
(rpc_server): cache layer improvement (#384)
Browse files Browse the repository at this point in the history
* experiment with chunk and block cache

* add info about chunks cache into health endpoint

* cache redident bytes size calculation

* cargo update

* reffactoring and optimization chache

* small improvement

* cargo update

* cache configuration
  • Loading branch information
kobayurii authored Jan 6, 2025
1 parent 2b9edee commit e7ab004
Show file tree
Hide file tree
Showing 18 changed files with 975 additions and 735 deletions.
855 changes: 427 additions & 428 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions configuration/src/configs/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,11 @@ impl CommonGeneralRpcServerConfig {
}

pub fn default_contract_code_cache_size() -> f64 {
0.25
2.0
}

pub fn default_block_cache_size() -> f64 {
0.125
3.0
}

pub fn default_shadow_data_consistency_rate() -> f64 {
Expand Down
10 changes: 6 additions & 4 deletions configuration/src/default_env_configs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ server_port = "${SERVER_PORT}"
max_gas_burnt = "${MAX_GAS_BURNT}"
## Contract code cache in gigabytes
## By default we use 0.25 gigabyte (256MB or 268_435_456 bytes)
## By default we use 2.0 gigabytes
## We divide the cache size 1/4 for contract code and 3/4 for compiled contract code
## Because the compiled contract code is bigger in 3 times than the contract code from the database
contract_code_cache_size = "${CONTRACT_CODE_CACHE_SIZE}"
## Block cache size in gigabytes
## By default we use 0.125 gigabyte (128MB or 134_217_728 bytes)
## One cache_block size is ≈ 96 bytes
## In 128MB we can put 1_398_101 cache_blocks
## By default we use 3 gigabytes
## We devide the cache size 1/3 for block cache and 2/3 for chunks cache
## Because the chunks for block is bigger in 2 times than the block
block_cache_size = "${BLOCK_CACHE_SIZE}"
## How many requests we should check for data consistency
Expand Down
17 changes: 12 additions & 5 deletions rpc-server/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ pub struct LruMemoryCache<K, V> {
max_size: usize,
}

impl<K: std::hash::Hash + Eq, V> LruMemoryCache<K, V> {
/// An indicator of the resident in memory of a value.
pub trait ResidentSize {
/// Return the resident size of the value. Users of the trait will depend
/// on this value to remain stable unless the value is mutated.
fn resident_size(&self) -> usize;
}

impl<K: std::hash::Hash + Eq, V: ResidentSize> LruMemoryCache<K, V> {
/// Create a new cache with a maximum memory size of values.
pub fn new(max_size: usize) -> Self {
LruMemoryCache {
Expand All @@ -23,7 +30,7 @@ impl<K: std::hash::Hash + Eq, V> LruMemoryCache<K, V> {
fn decrease(&mut self) {
while self.current_size > self.max_size {
match self.inner.pop_lru() {
Some((_, v)) => self.current_size -= std::mem::size_of_val(&v),
Some((_, v)) => self.current_size -= v.resident_size(),
_ => break,
}
}
Expand All @@ -43,11 +50,11 @@ impl<K: std::hash::Hash + Eq, V> LruMemoryCache<K, V> {
self.inner.resize(new_cap);
}

self.current_size += std::mem::size_of_val(&val);
self.current_size += val.resident_size();

// subtract any element displaced from the hash.
if let Some(lru) = self.inner.put(key, val) {
self.current_size -= std::mem::size_of_val(&lru);
self.current_size -= lru.resident_size();
}

self.decrease();
Expand Down Expand Up @@ -86,7 +93,7 @@ pub struct RwLockLruMemoryCache<K, V> {
inner: futures_locks::RwLock<LruMemoryCache<K, V>>,
}

impl<K: std::hash::Hash + Eq, V: Clone> RwLockLruMemoryCache<K, V> {
impl<K: std::hash::Hash + Eq, V: ResidentSize + Clone> RwLockLruMemoryCache<K, V> {
pub fn new(max_size: usize) -> Self {
RwLockLruMemoryCache {
inner: futures_locks::RwLock::new(LruMemoryCache::new(max_size)),
Expand Down
78 changes: 64 additions & 14 deletions rpc-server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use std::string::ToString;
use futures::executor::block_on;
use near_primitives::epoch_manager::{AllEpochConfig, EpochConfig};

use crate::modules::blocks::{BlocksInfoByFinality, CacheBlock};
use crate::utils;
use crate::modules::blocks::BlocksInfoByFinality;

static NEARD_VERSION: &str = env!("CARGO_PKG_VERSION");
static NEARD_BUILD: &str = env!("BUILD_VERSION");
Expand All @@ -15,7 +14,7 @@ static RUSTC_VERSION: &str = env!("RUSTC_VERSION");
#[derive(Debug, Clone)]
pub struct GenesisInfo {
pub genesis_config: near_chain_configs::GenesisConfig,
pub genesis_block_cache: CacheBlock,
pub genesis_block: near_primitives::views::BlockView,
}

impl GenesisInfo {
Expand All @@ -37,7 +36,7 @@ impl GenesisInfo {

Self {
genesis_config,
genesis_block_cache: CacheBlock::from(&genesis_block.block),
genesis_block: genesis_block.block,
}
}
}
Expand All @@ -56,8 +55,12 @@ pub struct ServerContext {
pub genesis_info: GenesisInfo,
/// Near rpc client
pub near_rpc_client: crate::utils::JsonRpcClient,
/// Blocks cache
pub blocks_cache: std::sync::Arc<crate::cache::RwLockLruMemoryCache<u64, CacheBlock>>,
/// Blocks cache. Store block_view by block_height
pub blocks_cache:
std::sync::Arc<crate::cache::RwLockLruMemoryCache<u64, near_primitives::views::BlockView>>,
/// Chunks cache. Store vector of block chunks by block_height
pub chunks_cache:
std::sync::Arc<crate::cache::RwLockLruMemoryCache<u64, crate::modules::blocks::ChunksInfo>>,
/// Final block info include final_block_cache and current_validators_info
pub blocks_info_by_finality: std::sync::Arc<BlocksInfoByFinality>,
/// Cache to store compiled contract codes
Expand All @@ -83,18 +86,42 @@ pub struct ServerContext {

impl ServerContext {
pub async fn init(rpc_server_config: configuration::RpcServerConfig) -> anyhow::Result<Self> {
let total_contract_code_cache_size_in_bytes =
crate::utils::gigabytes_to_bytes(rpc_server_config.general.contract_code_cache_size)
.await;

// For compiled contract code cache we use 3/4 of total_contract_code_cache_size_in_bytes
// because the compiled contract code is bigger in 3 times than the contract code from the database
let compiled_contract_code_cache_size_in_bytes =
total_contract_code_cache_size_in_bytes / 4;
let compiled_contract_code_cache = std::sync::Arc::new(CompiledCodeCache::new(
compiled_contract_code_cache_size_in_bytes,
));

// For contract code cache we use 1/4 of total_contract_code_cache_size_in_bytes
let contract_code_cache_size_in_bytes =
utils::gigabytes_to_bytes(rpc_server_config.general.contract_code_cache_size).await;
total_contract_code_cache_size_in_bytes - compiled_contract_code_cache_size_in_bytes;
let contract_code_cache = std::sync::Arc::new(crate::cache::RwLockLruMemoryCache::new(
contract_code_cache_size_in_bytes,
));

let block_cache_size_in_bytes =
utils::gigabytes_to_bytes(rpc_server_config.general.block_cache_size).await;
let total_block_cache_size_in_bytes =
crate::utils::gigabytes_to_bytes(rpc_server_config.general.block_cache_size).await;

// For block cache we use 1/3 of total_block_cache_size_in_bytes
let block_cache_size_in_bytes = total_block_cache_size_in_bytes / 3;
let blocks_cache = std::sync::Arc::new(crate::cache::RwLockLruMemoryCache::new(
block_cache_size_in_bytes,
));
let near_rpc_client = utils::JsonRpcClient::new(

// For chunk cache we use 2/3 of total_block_cache_size_in_bytes
// because the chunks for block is bigger in 2 times than the block
let chunk_cache_size_in_bytes = total_block_cache_size_in_bytes - block_cache_size_in_bytes;
let chunks_cache = std::sync::Arc::new(crate::cache::RwLockLruMemoryCache::new(
chunk_cache_size_in_bytes,
));

let near_rpc_client = crate::utils::JsonRpcClient::new(
rpc_server_config.general.near_rpc_url.clone(),
rpc_server_config.general.near_archival_rpc_url.clone(),
);
Expand Down Expand Up @@ -137,8 +164,9 @@ impl ServerContext {
);
let epoch_config = all_epoch_config.for_protocol_version(
blocks_info_by_finality
.final_cache_block()
.final_block_view()
.await
.header
.latest_protocol_version,
);

Expand All @@ -148,9 +176,6 @@ impl ServerContext {
)
.await?;

let compiled_contract_code_cache =
std::sync::Arc::new(CompiledCodeCache::new(contract_code_cache_size_in_bytes));

crate::metrics::CARGO_PKG_VERSION
.with_label_values(&[NEARD_VERSION])
.inc();
Expand All @@ -163,6 +188,7 @@ impl ServerContext {
genesis_info,
near_rpc_client,
blocks_cache,
chunks_cache,
blocks_info_by_finality,
compiled_contract_code_cache,
contract_code_cache,
Expand All @@ -181,6 +207,30 @@ impl ServerContext {
}
}

impl crate::cache::ResidentSize for near_vm_runner::CompiledContractInfo {
fn resident_size(&self) -> usize {
borsh::to_vec(self).unwrap_or_default().len()
}
}

impl crate::cache::ResidentSize for Vec<u8> {
fn resident_size(&self) -> usize {
self.len()
}
}

impl crate::cache::ResidentSize for crate::modules::blocks::ChunksInfo {
fn resident_size(&self) -> usize {
serde_json::to_vec(self).unwrap_or_default().len()
}
}

impl crate::cache::ResidentSize for near_primitives::views::BlockView {
fn resident_size(&self) -> usize {
serde_json::to_vec(self).unwrap_or_default().len()
}
}

#[derive(Clone)]
pub struct CompiledCodeCache {
pub local_cache: std::sync::Arc<
Expand Down
17 changes: 15 additions & 2 deletions rpc-server/src/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ pub struct RPCHealthStatusResponse {
max_blocks_cache_size: String,
current_blocks_cache_size: String,

chunks_in_cache: usize,
max_chunks_cache_size: String,
current_chunks_cache_size: String,

contracts_codes_in_cache: usize,
max_contracts_codes_cache_size: String,
current_contracts_codes_cache_size: String,
Expand All @@ -30,6 +34,14 @@ impl RPCHealthStatusResponse {
server_context.blocks_cache.current_size().await,
),

chunks_in_cache: server_context.chunks_cache.len().await,
max_chunks_cache_size: friendly_memory_size_format(
server_context.chunks_cache.max_size().await,
),
current_chunks_cache_size: friendly_memory_size_format(
server_context.chunks_cache.current_size().await,
),

contracts_codes_in_cache: server_context.contract_code_cache.len().await,
max_contracts_codes_cache_size: friendly_memory_size_format(
server_context.contract_code_cache.max_size().await,
Expand Down Expand Up @@ -60,9 +72,10 @@ impl RPCHealthStatusResponse {

final_block_height: server_context
.blocks_info_by_finality
.final_cache_block()
.final_block_view()
.await
.block_height,
.header
.height,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions rpc-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ async fn main() -> anyhow::Result<()> {
utils::task_regularly_update_blocks_by_finality(
std::sync::Arc::clone(&server_context.blocks_info_by_finality),
std::sync::Arc::clone(&server_context.blocks_cache),
std::sync::Arc::clone(&server_context.chunks_cache),
server_context.fastnear_client.clone(),
server_context.near_rpc_client.clone(),
)
Expand Down
6 changes: 3 additions & 3 deletions rpc-server/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,12 @@ pub async fn increase_request_category_metrics(
) {
match block_reference {
near_primitives::types::BlockReference::BlockId(_) => {
let final_block = data.blocks_info_by_finality.final_cache_block().await;
let final_block = data.blocks_info_by_finality.final_block_view().await;
let expected_earliest_available_block =
final_block.block_height - 5 * data.genesis_info.genesis_config.epoch_length;
final_block.header.height - 5 * data.genesis_info.genesis_config.epoch_length;
// By default, all requests should be historical, therefore
// if block_height is None we use `genesis.block_height` by default
if block_height.unwrap_or(data.genesis_info.genesis_block_cache.block_height)
if block_height.unwrap_or(data.genesis_info.genesis_block.header.height)
> expected_earliest_available_block
{
// This is request to regular nodes which includes 5 last epochs
Expand Down
Loading

0 comments on commit e7ab004

Please sign in to comment.