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,7 @@ 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 ( pooler, pooler . storage_buffer_pool ( ) . clone ( ) , page_size, capacity)
193205 }
194206
195207 /// The page size used by this page cache.
@@ -204,6 +216,12 @@ impl CacheRef {
204216 & self . pool
205217 }
206218
219+ /// Returns the number of page faults (cache misses) that have occurred.
220+ #[ cfg( test) ]
221+ pub fn page_faults ( & self ) -> u64 {
222+ self . page_faults . get ( )
223+ }
224+
207225 /// Returns a unique id for the next blob that will use this page cache.
208226 pub fn next_id ( & self ) -> u64 {
209227 self . next_id . fetch_add ( 1 , Ordering :: Relaxed )
@@ -285,6 +303,7 @@ impl CacheRef {
285303
286304 let ( page_num, offset_in_page) = Cache :: offset_to_page ( self . page_size , offset) ;
287305 let offset_in_page = offset_in_page as usize ;
306+ self . page_faults . inc ( ) ;
288307 trace ! ( page_num, blob_id, "page fault" ) ;
289308
290309 // Create or clone a future that retrieves the desired page from the underlying blob. This
@@ -759,6 +778,7 @@ mod tests {
759778 let cache_ref = CacheRef :: from_pooler ( & context, PAGE_SIZE , NZUsize ! ( 10 ) ) ;
760779 assert_eq ! ( cache_ref. next_id( ) , 0 ) ;
761780 assert_eq ! ( cache_ref. next_id( ) , 1 ) ;
781+ assert_eq ! ( cache_ref. page_faults( ) , 0 ) ;
762782 for i in 0 ..11 {
763783 // Read expects logical bytes only (CRCs are stripped).
764784 let mut buf = vec ! [ 0 ; PAGE_SIZE . get( ) as usize ] ;
@@ -768,6 +788,8 @@ mod tests {
768788 . unwrap ( ) ;
769789 assert_eq ! ( buf, [ i as u8 ; PAGE_SIZE . get( ) as usize ] ) ;
770790 }
791+ // 11 pages read, each was a cache miss.
792+ assert_eq ! ( cache_ref. page_faults( ) , 11 ) ;
771793
772794 // Repeat the read to exercise reading from the page cache. Must start at 1 because
773795 // page 0 should be evicted.
@@ -779,6 +801,8 @@ mod tests {
779801 . unwrap ( ) ;
780802 assert_eq ! ( buf, [ i as u8 ; PAGE_SIZE . get( ) as usize ] ) ;
781803 }
804+ // All 10 pages were cached, no new faults.
805+ assert_eq ! ( cache_ref. page_faults( ) , 11 ) ;
782806
783807 // Cleanup.
784808 blob. sync ( ) . await . unwrap ( ) ;
0 commit comments