@@ -3,7 +3,7 @@ use alloy_trie::Nibbles;
33use std:: {
44 collections:: HashMap ,
55 num:: NonZeroUsize ,
6- sync:: { Arc , RwLock , RwLockReadGuard } ,
6+ sync:: { Arc , RwLock } ,
77} ;
88
99/// An entry in the versioned LRU cache with doubly-linked list indices.
@@ -115,7 +115,7 @@ impl VersionedLru {
115115 . iter ( )
116116 . position ( |entry| entry. key == tail_key && entry. snapshot_id == smallest) ;
117117
118- // Remove from entries hashmap
118+ // Remove from ` entries` hashmap
119119 if let Some ( evict_idx) = smallest_idx {
120120 if let Some ( versions) = self . entries . get_mut ( & tail_key) {
121121 versions. retain ( |e| e. snapshot_id != smallest) ;
@@ -162,7 +162,7 @@ impl VersionedLru {
162162 self . tail = prev_idx;
163163 }
164164
165- // Mark as removed (we could also compact the list, but this is simpler)
165+ // Mark as removed
166166 self . lru [ lru_idx] . lru_prev = None ;
167167 self . lru [ lru_idx] . lru_next = None ;
168168 }
@@ -184,7 +184,7 @@ impl VersionedLru {
184184 }
185185 }
186186
187- // Remove obsolete entries from LRU list
187+ // Remove from LRU list
188188 self . lru . retain ( |entry| entry. snapshot_id >= min_id) ;
189189
190190 // Rebuild LRU pointers after retention
@@ -215,68 +215,31 @@ impl CacheManager {
215215 CacheManager { cache : Arc :: new ( RwLock :: new ( VersionedLru :: new ( max_size. get ( ) ) ) ) }
216216 }
217217
218- /// Provides a reader handle to the cache.
219- /// Multiple readers can exist concurrently.
220- pub fn read ( & self ) -> Reader {
221- Reader { guard : self . cache . read ( ) . unwrap ( ) }
222- }
223-
224- /// Provides a writer handle to the cache.
225- /// This briefly acquires a lock to clone the cache, then releases it.
226- pub fn write ( & self ) -> Writer {
227- Writer { cache : Arc :: clone ( & self . cache ) }
228- }
229-
230- /// Sets the minimum snapshot ID for proactive cache purging.
231- pub fn set_min_snapshot_id ( & self , min_snapshot_id : SnapshotId ) {
232- let mut guard = self . cache . write ( ) . unwrap ( ) ;
233- guard. set_min_snapshot_id ( min_snapshot_id) ;
234- }
235- }
236-
237- /// A handle for reading from the cache.
238- /// Dropping this struct releases the read lock.
239- #[ derive( Debug ) ]
240- pub struct Reader < ' a > {
241- guard : RwLockReadGuard < ' a , VersionedLru > ,
242- }
243-
244- impl < ' a > Reader < ' a > {
245- /// Gets a value for the given key and snapshot ID without updating LRU state.
218+ /// Gets a value for the given key and snapshot ID and updates LRU state.
246219 pub fn get ( & self , snapshot_id : SnapshotId , key : & Nibbles ) -> Option < ( PageId , u8 ) > {
247- let versions = self . guard . entries . get ( key) ?;
248- versions
249- . iter ( )
250- . rev ( )
251- . find ( |entry| entry. snapshot_id <= snapshot_id)
252- . and_then ( |entry| entry. value )
220+ // Acquire write lock since we move the `Entry` to the front of the LRU list each time
221+ // This is helpful because we'll want to cache an account on read to accelerate
222+ // reading its contract state.
223+ let mut guard = self . cache . write ( ) . unwrap ( ) ;
224+ guard. get ( key, snapshot_id)
253225 }
254- }
255226
256- /// A handle for writing to the cache.
257- /// Modifications are made directly to the shared cache under write lock.
258- #[ derive( Debug ) ]
259- pub struct Writer {
260- cache : Arc < RwLock < VersionedLru > > ,
261- }
262-
263- impl Writer {
264227 /// Inserts or updates an entry in the cache.
265- pub fn write ( & mut self , snapshot_id : SnapshotId , key : Nibbles , value : Option < ( PageId , u8 ) > ) {
228+ pub fn insert ( & self , snapshot_id : SnapshotId , key : Nibbles , value : Option < ( PageId , u8 ) > ) {
266229 let mut guard = self . cache . write ( ) . unwrap ( ) ;
267230 guard. set ( key, snapshot_id, value) ;
268231 }
269232
270233 /// Removes an entry from the cache by inserting a None value.
271- pub fn remove ( & mut self , snapshot_id : SnapshotId , key : Nibbles ) {
234+ pub fn remove ( & self , snapshot_id : SnapshotId , key : Nibbles ) {
272235 let mut guard = self . cache . write ( ) . unwrap ( ) ;
273236 guard. set ( key, snapshot_id, None ) ;
274237 }
275238
276- /// Gets a value and updates LRU state .
277- pub fn get ( & mut self , snapshot_id : SnapshotId , key : & Nibbles ) -> Option < ( PageId , u8 ) > {
239+ /// Sets the minimum snapshot ID for proactive cache purging .
240+ pub fn set_min_snapshot_id ( & self , min_snapshot_id : SnapshotId ) {
278241 let mut guard = self . cache . write ( ) . unwrap ( ) ;
279- guard. get ( key , snapshot_id )
242+ guard. set_min_snapshot_id ( min_snapshot_id ) ;
280243 }
281244}
282245
@@ -291,62 +254,64 @@ mod tests {
291254 let shared_cache = Arc :: new ( cache) ;
292255
293256 // first writer
294- let mut writer1 = shared_cache. write ( ) ;
295- writer1. write ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 10 ) . unwrap ( ) , 11 ) ) ) ;
296- writer1. write ( 100 , Nibbles :: from_nibbles ( [ 2 ] ) , Some ( ( PageId :: new ( 12 ) . unwrap ( ) , 13 ) ) ) ;
297- writer1. write ( 200 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 20 ) . unwrap ( ) , 21 ) ) ) ;
298- drop ( writer1) ;
257+ shared_cache. insert ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 10 ) . unwrap ( ) , 11 ) ) ) ;
258+ shared_cache. insert ( 100 , Nibbles :: from_nibbles ( [ 2 ] ) , Some ( ( PageId :: new ( 12 ) . unwrap ( ) , 13 ) ) ) ;
259+ shared_cache. insert ( 200 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 20 ) . unwrap ( ) , 21 ) ) ) ;
299260
300261 // have some concurrent readers
301262 let cache_reader1 = Arc :: clone ( & shared_cache) ;
302263 let reader1 = thread:: spawn ( move || {
303- let reader = cache_reader1. read ( ) ;
304- let val1 = reader. get ( 100 , & Nibbles :: from_nibbles ( [ 1 ] ) ) ;
305- let val2 = reader. get ( 200 , & Nibbles :: from_nibbles ( [ 1 ] ) ) ;
264+ let val1 = cache_reader1. get ( 100 , & Nibbles :: from_nibbles ( [ 1 ] ) ) ;
265+ let val2 = cache_reader1. get ( 200 , & Nibbles :: from_nibbles ( [ 1 ] ) ) ;
306266 assert_eq ! ( val1, Some ( ( PageId :: new( 10 ) . unwrap( ) , 11 ) ) ) ;
307267 assert_eq ! ( val2, Some ( ( PageId :: new( 20 ) . unwrap( ) , 21 ) ) ) ;
308268 thread:: sleep ( Duration :: from_millis ( 50 ) ) ;
309269 } ) ;
310270
311271 let cache_reader2 = Arc :: clone ( & shared_cache) ;
312272 let reader2 = thread:: spawn ( move || {
313- let reader = cache_reader2. read ( ) ;
314- let val = reader. get ( 100 , & Nibbles :: from_nibbles ( [ 2 ] ) ) ;
273+ let val = cache_reader2. get ( 100 , & Nibbles :: from_nibbles ( [ 2 ] ) ) ;
315274 assert_eq ! ( val, Some ( ( PageId :: new( 12 ) . unwrap( ) , 13 ) ) ) ;
316275 thread:: sleep ( Duration :: from_millis ( 100 ) ) ;
317276 } ) ;
318277
319- // writer2 will be blocked until concurrent readers are done
278+ // writer2
320279 let cache_writer2 = Arc :: clone ( & shared_cache) ;
321280 let writer2 = thread:: spawn ( move || {
322- let mut writer = cache_writer2. write ( ) ;
323- writer. write ( 101 , Nibbles :: from_nibbles ( [ 3 ] ) , Some ( ( PageId :: new ( 14 ) . unwrap ( ) , 15 ) ) ) ;
324- writer. write ( 300 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 30 ) . unwrap ( ) , 31 ) ) ) ;
281+ cache_writer2. insert (
282+ 101 ,
283+ Nibbles :: from_nibbles ( [ 3 ] ) ,
284+ Some ( ( PageId :: new ( 14 ) . unwrap ( ) , 15 ) ) ,
285+ ) ;
286+ cache_writer2. insert (
287+ 300 ,
288+ Nibbles :: from_nibbles ( [ 1 ] ) ,
289+ Some ( ( PageId :: new ( 30 ) . unwrap ( ) , 31 ) ) ,
290+ ) ;
325291 } ) ;
326292
327293 reader1. join ( ) . unwrap ( ) ;
328294 reader2. join ( ) . unwrap ( ) ;
329295 writer2. join ( ) . unwrap ( ) ;
330296
331- let final_reader = shared_cache. read ( ) ;
332297 assert_eq ! (
333- final_reader . get( 100 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
298+ shared_cache . get( 100 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
334299 Some ( ( PageId :: new( 10 ) . unwrap( ) , 11 ) )
335300 ) ;
336301 assert_eq ! (
337- final_reader . get( 100 , & Nibbles :: from_nibbles( [ 2 ] ) ) ,
302+ shared_cache . get( 100 , & Nibbles :: from_nibbles( [ 2 ] ) ) ,
338303 Some ( ( PageId :: new( 12 ) . unwrap( ) , 13 ) )
339304 ) ;
340305 assert_eq ! (
341- final_reader . get( 101 , & Nibbles :: from_nibbles( [ 3 ] ) ) ,
306+ shared_cache . get( 101 , & Nibbles :: from_nibbles( [ 3 ] ) ) ,
342307 Some ( ( PageId :: new( 14 ) . unwrap( ) , 15 ) )
343308 ) ;
344309 assert_eq ! (
345- final_reader . get( 200 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
310+ shared_cache . get( 200 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
346311 Some ( ( PageId :: new( 20 ) . unwrap( ) , 21 ) )
347312 ) ;
348313 assert_eq ! (
349- final_reader . get( 300 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
314+ shared_cache . get( 300 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
350315 Some ( ( PageId :: new( 30 ) . unwrap( ) , 31 ) )
351316 ) ;
352317 }
@@ -356,41 +321,36 @@ mod tests {
356321 let cache = CacheManager :: new ( NonZeroUsize :: new ( 10 ) . unwrap ( ) ) ;
357322 let shared_cache = Arc :: new ( cache) ;
358323
359- let mut writer = shared_cache. write ( ) ;
360- writer. write ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 10 ) . unwrap ( ) , 11 ) ) ) ;
361- writer. write ( 200 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 20 ) . unwrap ( ) , 21 ) ) ) ;
362- writer. write ( 300 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 30 ) . unwrap ( ) , 31 ) ) ) ;
363- drop ( writer) ;
364-
365- let reader = shared_cache. read ( ) ;
324+ shared_cache. insert ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 10 ) . unwrap ( ) , 11 ) ) ) ;
325+ shared_cache. insert ( 200 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 20 ) . unwrap ( ) , 21 ) ) ) ;
326+ shared_cache. insert ( 300 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 30 ) . unwrap ( ) , 31 ) ) ) ;
366327
367- // given the exact same snapshot
328+ // exact same snapshots
368329 assert_eq ! (
369- reader . get( 100 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
330+ shared_cache . get( 100 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
370331 Some ( ( PageId :: new( 10 ) . unwrap( ) , 11 ) )
371332 ) ;
372333 assert_eq ! (
373- reader . get( 200 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
334+ shared_cache . get( 200 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
374335 Some ( ( PageId :: new( 20 ) . unwrap( ) , 21 ) )
375336 ) ;
376337 assert_eq ! (
377- reader . get( 300 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
338+ shared_cache . get( 300 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
378339 Some ( ( PageId :: new( 30 ) . unwrap( ) , 31 ) )
379340 ) ;
380341
381- // given different snapshots, but it should find the latest version <= target snapshot
342+ // different snapshots, but it should find the latest version <= target snapshot
382343 assert_eq ! (
383- reader . get( 150 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
344+ shared_cache . get( 150 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
384345 Some ( ( PageId :: new( 10 ) . unwrap( ) , 11 ) )
385346 ) ;
386347 assert_eq ! (
387- reader . get( 250 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
348+ shared_cache . get( 250 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
388349 Some ( ( PageId :: new( 20 ) . unwrap( ) , 21 ) )
389350 ) ;
390351
391- // given snapshot too small, since snapshot < earliest
392- assert_eq ! ( reader. get( 50 , & Nibbles :: from_nibbles( [ 1 ] ) ) , None ) ;
393- drop ( reader) ;
352+ // snapshot too small, since snapshot < earliest
353+ assert_eq ! ( shared_cache. get( 50 , & Nibbles :: from_nibbles( [ 1 ] ) ) , None ) ;
394354 }
395355
396356 #[ test]
@@ -399,17 +359,13 @@ mod tests {
399359 let shared_cache = Arc :: new ( cache) ;
400360
401361 // insert a value
402- let mut writer = shared_cache. write ( ) ;
403- writer. write ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 10 ) . unwrap ( ) , 11 ) ) ) ;
362+ shared_cache. insert ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 10 ) . unwrap ( ) , 11 ) ) ) ;
404363
405364 // invalidate it
406- writer. write ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , None ) ;
407- drop ( writer) ;
365+ shared_cache. insert ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , None ) ;
408366
409367 // try reading it
410- let reader = shared_cache. read ( ) ;
411- assert_eq ! ( reader. get( 100 , & Nibbles :: from_nibbles( [ 1 ] ) ) , None ) ;
412- drop ( reader) ;
368+ assert_eq ! ( shared_cache. get( 100 , & Nibbles :: from_nibbles( [ 1 ] ) ) , None ) ;
413369 }
414370
415371 #[ test]
@@ -418,65 +374,58 @@ mod tests {
418374 let shared_cache = Arc :: new ( cache) ;
419375
420376 // insert entries with different snapshots
421- let mut writer = shared_cache. write ( ) ;
422- writer. write ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 10 ) . unwrap ( ) , 11 ) ) ) ;
423- writer. write ( 200 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 20 ) . unwrap ( ) , 21 ) ) ) ;
424- writer. write ( 300 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 30 ) . unwrap ( ) , 31 ) ) ) ;
425- drop ( writer) ;
377+ shared_cache. insert ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 10 ) . unwrap ( ) , 11 ) ) ) ;
378+ shared_cache. insert ( 200 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 20 ) . unwrap ( ) , 21 ) ) ) ;
379+ shared_cache. insert ( 300 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 30 ) . unwrap ( ) , 31 ) ) ) ;
426380
427381 // set minimum snapshot ID to 250
428382 shared_cache. set_min_snapshot_id ( 250 ) ;
429- let reader = shared_cache. read ( ) ;
430383
431384 // purged the entries below min snapshot
432- assert_eq ! ( reader . get( 100 , & Nibbles :: from_nibbles( [ 1 ] ) ) , None ) ;
433- assert_eq ! ( reader . get( 200 , & Nibbles :: from_nibbles( [ 1 ] ) ) , None ) ;
385+ assert_eq ! ( shared_cache . get( 100 , & Nibbles :: from_nibbles( [ 1 ] ) ) , None ) ;
386+ assert_eq ! ( shared_cache . get( 200 , & Nibbles :: from_nibbles( [ 1 ] ) ) , None ) ;
434387
435388 // only keep entries above min snapshot
436389 assert_eq ! (
437- reader . get( 300 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
390+ shared_cache . get( 300 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
438391 Some ( ( PageId :: new( 30 ) . unwrap( ) , 31 ) )
439392 ) ;
440- drop ( reader) ;
441393 }
442394
443395 #[ test]
444396 fn test_oldest_sibling_eviction ( ) {
445397 let cache = CacheManager :: new ( NonZeroUsize :: new ( 4 ) . unwrap ( ) ) ;
446398 let shared_cache = Arc :: new ( cache) ;
447399
448- let mut writer = shared_cache. write ( ) ;
449400 // multiple versions of key [1]
450- writer . write ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 10 ) . unwrap ( ) , 11 ) ) ) ;
451- writer . write ( 200 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 20 ) . unwrap ( ) , 21 ) ) ) ;
452- writer . write ( 300 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 30 ) . unwrap ( ) , 31 ) ) ) ;
401+ shared_cache . insert ( 100 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 10 ) . unwrap ( ) , 11 ) ) ) ;
402+ shared_cache . insert ( 200 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 20 ) . unwrap ( ) , 21 ) ) ) ;
403+ shared_cache . insert ( 300 , Nibbles :: from_nibbles ( [ 1 ] ) , Some ( ( PageId :: new ( 30 ) . unwrap ( ) , 31 ) ) ) ;
453404
454405 // one entry for key [2]
455- writer . write ( 150 , Nibbles :: from_nibbles ( [ 2 ] ) , Some ( ( PageId :: new ( 15 ) . unwrap ( ) , 16 ) ) ) ;
406+ shared_cache . insert ( 150 , Nibbles :: from_nibbles ( [ 2 ] ) , Some ( ( PageId :: new ( 15 ) . unwrap ( ) , 16 ) ) ) ;
456407
457408 // since the cache is full, should evict oldest sibling of tail entry
458- writer. write ( 400 , Nibbles :: from_nibbles ( [ 3 ] ) , Some ( ( PageId :: new ( 40 ) . unwrap ( ) , 41 ) ) ) ;
459- drop ( writer) ;
409+ shared_cache. insert ( 400 , Nibbles :: from_nibbles ( [ 3 ] ) , Some ( ( PageId :: new ( 40 ) . unwrap ( ) , 41 ) ) ) ;
460410
461- let reader = shared_cache. read ( ) ;
462- // the oldest sibling (snapshot 100) should be evicted, NOT the tail entry
463- assert_eq ! ( reader. get( 100 , & Nibbles :: from_nibbles( [ 1 ] ) ) , None ) ;
411+ // snapshot 100 should be evicted
412+ assert_eq ! ( shared_cache. get( 100 , & Nibbles :: from_nibbles( [ 1 ] ) ) , None ) ;
464413
465- // test the rest should exist
414+ // rest should exist
466415 assert_eq ! (
467- reader . get( 200 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
416+ shared_cache . get( 200 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
468417 Some ( ( PageId :: new( 20 ) . unwrap( ) , 21 ) )
469418 ) ;
470419 assert_eq ! (
471- reader . get( 300 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
420+ shared_cache . get( 300 , & Nibbles :: from_nibbles( [ 1 ] ) ) ,
472421 Some ( ( PageId :: new( 30 ) . unwrap( ) , 31 ) )
473422 ) ;
474423 assert_eq ! (
475- reader . get( 150 , & Nibbles :: from_nibbles( [ 2 ] ) ) ,
424+ shared_cache . get( 150 , & Nibbles :: from_nibbles( [ 2 ] ) ) ,
476425 Some ( ( PageId :: new( 15 ) . unwrap( ) , 16 ) )
477426 ) ;
478427 assert_eq ! (
479- reader . get( 400 , & Nibbles :: from_nibbles( [ 3 ] ) ) ,
428+ shared_cache . get( 400 , & Nibbles :: from_nibbles( [ 3 ] ) ) ,
480429 Some ( ( PageId :: new( 40 ) . unwrap( ) , 41 ) )
481430 ) ;
482431 }
0 commit comments