22//! physical page format used by the blob, which is left to the blob implementation.
33
44use super :: get_page_from_blob;
5- use crate :: { Blob , BufferPool , BufferPooler , Error , IoBuf , IoBufMut } ;
5+ use crate :: { Blob , BufferPool , BufferPooler , Error , IoBuf , IoBufMut , Metrics } ;
66use commonware_utils:: sync:: RwLock ;
77use futures:: { future:: Shared , FutureExt } ;
8+ use prometheus_client:: metrics:: counter:: Counter ;
89use std:: {
910 collections:: { hash_map:: Entry , HashMap } ,
1011 future:: Future ,
@@ -162,6 +163,9 @@ pub struct CacheRef {
162163 /// Shareable reference to the page cache.
163164 cache : Arc < RwLock < Cache > > ,
164165
166+ /// Number of page faults (cache misses that enter the fault handler).
167+ page_faults : Counter ,
168+
165169 /// Pool used for page-cache and associated buffer allocations.
166170 pool : BufferPool ,
167171}
@@ -171,13 +175,22 @@ impl CacheRef {
171175 ///
172176 /// The cache stores at most `capacity` pages, each exactly `page_size` bytes.
173177 /// Initialization eagerly allocates and zeroes all cache slots from `pool`.
174- pub fn new ( pool : BufferPool , page_size : NonZeroU16 , capacity : NonZeroUsize ) -> Self {
178+ pub fn new (
179+ context : & impl Metrics ,
180+ pool : BufferPool ,
181+ page_size : NonZeroU16 ,
182+ capacity : NonZeroUsize ,
183+ ) -> Self {
175184 let page_size_u64 = page_size. get ( ) as u64 ;
185+ let page_faults = Counter :: default ( ) ;
186+ let context = context. with_label ( "page_cache" ) ;
187+ context. register ( "page_faults" , "Number of page faults" , page_faults. clone ( ) ) ;
176188
177189 Self {
178190 page_size : page_size_u64,
179191 next_id : Arc :: new ( AtomicU64 :: new ( 0 ) ) ,
180192 cache : Arc :: new ( RwLock :: new ( Cache :: new ( pool. clone ( ) , page_size, capacity) ) ) ,
193+ page_faults,
181194 pool,
182195 }
183196 }
@@ -189,7 +202,12 @@ impl CacheRef {
189202 page_size : NonZeroU16 ,
190203 capacity : NonZeroUsize ,
191204 ) -> Self {
192- Self :: new ( pooler. storage_buffer_pool ( ) . clone ( ) , page_size, capacity)
205+ Self :: new (
206+ pooler,
207+ pooler. storage_buffer_pool ( ) . clone ( ) ,
208+ page_size,
209+ capacity,
210+ )
193211 }
194212
195213 /// The page size used by this page cache.
@@ -204,6 +222,12 @@ impl CacheRef {
204222 & self . pool
205223 }
206224
225+ /// Returns the number of page faults (cache misses) that have occurred.
226+ #[ cfg( test) ]
227+ pub fn page_faults ( & self ) -> u64 {
228+ self . page_faults . get ( )
229+ }
230+
207231 /// Returns a unique id for the next blob that will use this page cache.
208232 pub fn next_id ( & self ) -> u64 {
209233 self . next_id . fetch_add ( 1 , Ordering :: Relaxed )
@@ -285,6 +309,7 @@ impl CacheRef {
285309
286310 let ( page_num, offset_in_page) = Cache :: offset_to_page ( self . page_size , offset) ;
287311 let offset_in_page = offset_in_page as usize ;
312+ self . page_faults . inc ( ) ;
288313 trace ! ( page_num, blob_id, "page fault" ) ;
289314
290315 // Create or clone a future that retrieves the desired page from the underlying blob. This
@@ -759,6 +784,7 @@ mod tests {
759784 let cache_ref = CacheRef :: from_pooler ( & context, PAGE_SIZE , NZUsize ! ( 10 ) ) ;
760785 assert_eq ! ( cache_ref. next_id( ) , 0 ) ;
761786 assert_eq ! ( cache_ref. next_id( ) , 1 ) ;
787+ assert_eq ! ( cache_ref. page_faults( ) , 0 ) ;
762788 for i in 0 ..11 {
763789 // Read expects logical bytes only (CRCs are stripped).
764790 let mut buf = vec ! [ 0 ; PAGE_SIZE . get( ) as usize ] ;
@@ -768,6 +794,8 @@ mod tests {
768794 . unwrap ( ) ;
769795 assert_eq ! ( buf, [ i as u8 ; PAGE_SIZE . get( ) as usize ] ) ;
770796 }
797+ // 11 pages read, each was a cache miss.
798+ assert_eq ! ( cache_ref. page_faults( ) , 11 ) ;
771799
772800 // Repeat the read to exercise reading from the page cache. Must start at 1 because
773801 // page 0 should be evicted.
@@ -779,6 +807,8 @@ mod tests {
779807 . unwrap ( ) ;
780808 assert_eq ! ( buf, [ i as u8 ; PAGE_SIZE . get( ) as usize ] ) ;
781809 }
810+ // All 10 pages were cached, no new faults.
811+ assert_eq ! ( cache_ref. page_faults( ) , 11 ) ;
782812
783813 // Cleanup.
784814 blob. sync ( ) . await . unwrap ( ) ;
0 commit comments