Skip to content

Commit 6ef14c8

Browse files
pablodeymojrchatrucedg-lMegaRedHand
authored
feat(l1): code cache rocksdb (#5307)
**Motivation** Adds a code cache to rocksdb, evicting the least used entries if size goes over 64mb --------- Co-authored-by: Javier Chatruc <[email protected]> Co-authored-by: Edgar <[email protected]> Co-authored-by: Tomás Grüner <[email protected]>
1 parent 3af2dba commit 6ef14c8

File tree

7 files changed

+185
-7
lines changed

7 files changed

+185
-7
lines changed

Cargo.lock

Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/common/types/account.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,21 @@ impl Code {
6565
}
6666
targets
6767
}
68+
69+
/// Estimates the size of the Code struct in bytes
70+
/// (including stack size and heap allocation).
71+
///
72+
/// Note: This is an estimation and may not be exact.
73+
///
74+
/// # Returns
75+
///
76+
/// usize - Estimated size in bytes
77+
pub fn size(&self) -> usize {
78+
let hash_size = size_of::<H256>();
79+
let bytes_size = size_of::<Bytes>();
80+
let vec_size = size_of::<Vec<u32>>() + self.jump_targets.len() * size_of::<u32>();
81+
hash_size + bytes_size + vec_size
82+
}
6883
}
6984

7085
impl AsRef<Bytes> for Code {

crates/l2/prover/src/guest_program/src/risc0/Cargo.lock

Lines changed: 22 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/l2/prover/src/guest_program/src/sp1/Cargo.lock

Lines changed: 22 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/l2/tee/quote-gen/Cargo.lock

Lines changed: 29 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/storage/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ tokio = { workspace = true, optional = true, features = ["rt"] }
2828
bincode = "1.3.3"
2929
qfilter = "0.2.5"
3030
rayon.workspace = true
31+
lru = "0.16.2"
3132

3233
[features]
3334
default = []

crates/storage/store_db/rocksdb.rs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@ use ethrex_common::{
1414
},
1515
};
1616
use ethrex_trie::{Nibbles, Node, Trie};
17+
use lru::LruCache;
1718
use rocksdb::{
1819
BlockBasedOptions, BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded,
1920
Options, WriteBatch, checkpoint::Checkpoint,
2021
};
22+
use rustc_hash::FxBuildHasher;
2123
use std::{
2224
collections::HashSet,
2325
path::{Path, PathBuf},
2426
sync::{
2527
Arc, Mutex,
28+
atomic::AtomicU64,
2629
mpsc::{SyncSender, sync_channel},
2730
},
2831
};
@@ -149,13 +152,40 @@ enum FKVGeneratorControlMessage {
149152
Continue,
150153
}
151154

152-
#[derive(Debug, Clone)]
155+
// 64mb
156+
const CODE_CACHE_MAX_SIZE: u64 = 64 * 1024 * 1024;
157+
type CodeCache = LruCache<H256, Code, FxBuildHasher>;
158+
159+
#[derive(Debug)]
153160
pub struct Store {
154161
db: Arc<DBWithThreadMode<MultiThreaded>>,
155162
trie_cache: Arc<Mutex<Arc<TrieLayerCache>>>,
156163
flatkeyvalue_control_tx: std::sync::mpsc::SyncSender<FKVGeneratorControlMessage>,
157164
trie_update_worker_tx: TriedUpdateWorkerTx,
158165
last_computed_flatkeyvalue: Arc<Mutex<Vec<u8>>>,
166+
/// Cache for account bytecodes, keyed by the bytecode hash.
167+
/// Note that we don't remove entries on account code changes, since
168+
/// those changes already affect the code hash stored in the account, and only
169+
/// may result in this cache having useless data.
170+
account_code_cache: Arc<Mutex<CodeCache>>,
171+
account_code_cache_size: AtomicU64,
172+
}
173+
174+
impl Clone for Store {
175+
fn clone(&self) -> Self {
176+
Self {
177+
db: self.db.clone(),
178+
trie_cache: self.trie_cache.clone(),
179+
flatkeyvalue_control_tx: self.flatkeyvalue_control_tx.clone(),
180+
trie_update_worker_tx: self.trie_update_worker_tx.clone(),
181+
last_computed_flatkeyvalue: self.last_computed_flatkeyvalue.clone(),
182+
account_code_cache: self.account_code_cache.clone(),
183+
account_code_cache_size: AtomicU64::new(
184+
self.account_code_cache_size
185+
.load(std::sync::atomic::Ordering::Relaxed),
186+
),
187+
}
188+
}
159189
}
160190

161191
impl Store {
@@ -399,6 +429,10 @@ impl Store {
399429
flatkeyvalue_control_tx: fkv_tx,
400430
trie_update_worker_tx: trie_upd_tx,
401431
last_computed_flatkeyvalue: Arc::new(Mutex::new(last_written)),
432+
account_code_cache: Arc::new(Mutex::new(LruCache::unbounded_with_hasher(
433+
FxBuildHasher,
434+
))),
435+
account_code_cache_size: AtomicU64::new(0),
402436
};
403437
let store_clone = store.clone();
404438
std::thread::spawn(move || {
@@ -1363,7 +1397,21 @@ impl StoreEngine for Store {
13631397
.map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
13641398
}
13651399

1400+
/// Get account code by its hash.
1401+
///
1402+
/// Check if the code exists in the cache (attribute `account_code_cache`), if not,
1403+
/// reads the database, and if it exists, decodes and returns it.
13661404
fn get_account_code(&self, code_hash: H256) -> Result<Option<Code>, StoreError> {
1405+
// check cache first
1406+
if let Some(code) = self
1407+
.account_code_cache
1408+
.lock()
1409+
.map_err(|_| StoreError::LockError)?
1410+
.get(&code_hash)
1411+
{
1412+
return Ok(Some(code.clone()));
1413+
}
1414+
13671415
let cf = self.cf_handle(CF_ACCOUNT_CODES)?;
13681416
let Some(bytes) = self
13691417
.db
@@ -1378,6 +1426,39 @@ impl StoreEngine for Store {
13781426
bytecode: Bytes::copy_from_slice(bytecode),
13791427
jump_targets: <Vec<_>>::decode(targets)?,
13801428
};
1429+
1430+
// insert into cache and evict if needed
1431+
{
1432+
let mut cache = self
1433+
.account_code_cache
1434+
.lock()
1435+
.map_err(|_| StoreError::LockError)?;
1436+
let code_size = code.size();
1437+
self.account_code_cache_size
1438+
.fetch_add(code_size as u64, std::sync::atomic::Ordering::SeqCst);
1439+
let cache_len = cache.len() + 1;
1440+
let mut current_size = self
1441+
.account_code_cache_size
1442+
.load(std::sync::atomic::Ordering::SeqCst);
1443+
debug!(
1444+
"[ACCOUNT CODE CACHE] cache elements (): {cache_len}, total size: {current_size} bytes"
1445+
);
1446+
1447+
while current_size > CODE_CACHE_MAX_SIZE {
1448+
if let Some((_, code)) = cache.pop_lru() {
1449+
self.account_code_cache_size
1450+
.fetch_sub(code.size() as u64, std::sync::atomic::Ordering::SeqCst);
1451+
current_size = self
1452+
.account_code_cache_size
1453+
.load(std::sync::atomic::Ordering::SeqCst);
1454+
} else {
1455+
break;
1456+
}
1457+
}
1458+
1459+
cache.get_or_insert(code_hash, || code.clone());
1460+
}
1461+
13811462
Ok(Some(code))
13821463
}
13831464

0 commit comments

Comments
 (0)