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,21 @@ 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+ context. register ( "page_faults" , "Number of page faults" , page_faults. clone ( ) ) ;
176187
177188 Self {
178189 page_size : page_size_u64,
179190 next_id : Arc :: new ( AtomicU64 :: new ( 0 ) ) ,
180191 cache : Arc :: new ( RwLock :: new ( Cache :: new ( pool. clone ( ) , page_size, capacity) ) ) ,
192+ page_faults,
181193 pool,
182194 }
183195 }
@@ -189,7 +201,12 @@ impl CacheRef {
189201 page_size : NonZeroU16 ,
190202 capacity : NonZeroUsize ,
191203 ) -> Self {
192- Self :: new ( pooler. storage_buffer_pool ( ) . clone ( ) , page_size, capacity)
204+ Self :: new (
205+ pooler,
206+ pooler. storage_buffer_pool ( ) . clone ( ) ,
207+ page_size,
208+ capacity,
209+ )
193210 }
194211
195212 /// The page size used by this page cache.
@@ -204,6 +221,12 @@ impl CacheRef {
204221 & self . pool
205222 }
206223
224+ /// Returns the number of page faults (cache misses) that have occurred.
225+ #[ cfg( test) ]
226+ pub fn page_faults ( & self ) -> u64 {
227+ self . page_faults . get ( )
228+ }
229+
207230 /// Returns a unique id for the next blob that will use this page cache.
208231 pub fn next_id ( & self ) -> u64 {
209232 self . next_id . fetch_add ( 1 , Ordering :: Relaxed )
@@ -285,6 +308,7 @@ impl CacheRef {
285308
286309 let ( page_num, offset_in_page) = Cache :: offset_to_page ( self . page_size , offset) ;
287310 let offset_in_page = offset_in_page as usize ;
311+ self . page_faults . inc ( ) ;
288312 trace ! ( page_num, blob_id, "page fault" ) ;
289313
290314 // Create or clone a future that retrieves the desired page from the underlying blob. This
@@ -759,6 +783,7 @@ mod tests {
759783 let cache_ref = CacheRef :: from_pooler ( & context, PAGE_SIZE , NZUsize ! ( 10 ) ) ;
760784 assert_eq ! ( cache_ref. next_id( ) , 0 ) ;
761785 assert_eq ! ( cache_ref. next_id( ) , 1 ) ;
786+ assert_eq ! ( cache_ref. page_faults( ) , 0 ) ;
762787 for i in 0 ..11 {
763788 // Read expects logical bytes only (CRCs are stripped).
764789 let mut buf = vec ! [ 0 ; PAGE_SIZE . get( ) as usize ] ;
@@ -768,6 +793,8 @@ mod tests {
768793 . unwrap ( ) ;
769794 assert_eq ! ( buf, [ i as u8 ; PAGE_SIZE . get( ) as usize ] ) ;
770795 }
796+ // 11 pages read, each was a cache miss.
797+ assert_eq ! ( cache_ref. page_faults( ) , 11 ) ;
771798
772799 // Repeat the read to exercise reading from the page cache. Must start at 1 because
773800 // page 0 should be evicted.
@@ -779,6 +806,8 @@ mod tests {
779806 . unwrap ( ) ;
780807 assert_eq ! ( buf, [ i as u8 ; PAGE_SIZE . get( ) as usize ] ) ;
781808 }
809+ // All 10 pages were cached, no new faults.
810+ assert_eq ! ( cache_ref. page_faults( ) , 11 ) ;
782811
783812 // Cleanup.
784813 blob. sync ( ) . await . unwrap ( ) ;
0 commit comments