@@ -106,6 +106,11 @@ impl<F: MetadataFetch> ReadaheadMetadataCache<F> {
106106 }
107107 }
108108
109+ /// Access the inner MetadataFetch
110+ pub fn inner ( & self ) -> & F {
111+ & self . inner
112+ }
113+
109114 /// Set the initial fetch size in bytes, otherwise defaults to 32 KiB
110115 pub fn with_initial_size ( mut self , initial : u64 ) -> Self {
111116 self . initial = initial;
@@ -154,3 +159,79 @@ impl<F: MetadataFetch + Send + Sync> MetadataFetch for ReadaheadMetadataCache<F>
154159 } )
155160 }
156161}
162+
163+ #[ cfg( test) ]
164+ mod test {
165+ use futures:: future:: FutureExt ;
166+
167+ use super :: * ;
168+
169+ struct TestFetch {
170+ data : Bytes ,
171+ /// The number of fetches that actually reach the raw Fetch implementation
172+ num_fetches : Arc < Mutex < u64 > > ,
173+ }
174+
175+ impl TestFetch {
176+ fn new ( data : Bytes ) -> Self {
177+ Self {
178+ data,
179+ num_fetches : Arc :: new ( Mutex :: new ( 0 ) ) ,
180+ }
181+ }
182+ }
183+
184+ impl MetadataFetch for TestFetch {
185+ fn fetch (
186+ & self ,
187+ range : Range < u64 > ,
188+ ) -> futures:: future:: BoxFuture < ' _ , crate :: error:: AsyncTiffResult < Bytes > > {
189+ if range. start as usize >= self . data . len ( ) {
190+ return async { Ok ( Bytes :: new ( ) ) } . boxed ( ) ;
191+ }
192+
193+ let end = ( range. end as usize ) . min ( self . data . len ( ) ) ;
194+ let slice = self . data . slice ( range. start as _ ..end) ;
195+ async move {
196+ let mut g = self . num_fetches . lock ( ) . await ;
197+ * g += 1 ;
198+ Ok ( slice)
199+ }
200+ . boxed ( )
201+ }
202+ }
203+
204+ #[ tokio:: test]
205+ async fn test_readahead_cache ( ) {
206+ let data = Bytes :: from_static ( b"abcdefghijklmnopqrstuvwxyz" ) ;
207+ let fetch = TestFetch :: new ( data. clone ( ) ) ;
208+ let cache = ReadaheadMetadataCache :: new ( fetch)
209+ . with_initial_size ( 2 )
210+ . with_multiplier ( 3.0 ) ;
211+
212+ // Make initial request
213+ let result = cache. fetch ( 0 ..2 ) . await . unwrap ( ) ;
214+ assert_eq ! ( result. as_ref( ) , b"ab" ) ;
215+ assert_eq ! ( * cache. inner. num_fetches. lock( ) . await , 1 ) ;
216+
217+ // Making a request within the cached range should not trigger a new fetch
218+ let result = cache. fetch ( 1 ..2 ) . await . unwrap ( ) ;
219+ assert_eq ! ( result. as_ref( ) , b"b" ) ;
220+ assert_eq ! ( * cache. inner. num_fetches. lock( ) . await , 1 ) ;
221+
222+ // Making a request that exceeds the cached range should trigger a new fetch
223+ let result = cache. fetch ( 2 ..5 ) . await . unwrap ( ) ;
224+ assert_eq ! ( result. as_ref( ) , b"cde" ) ;
225+ assert_eq ! ( * cache. inner. num_fetches. lock( ) . await , 2 ) ;
226+
227+ // Multiplier should be accurate: initial was 2, next was 6 (2*3), so total cached is now 8
228+ let result = cache. fetch ( 5 ..8 ) . await . unwrap ( ) ;
229+ assert_eq ! ( result. as_ref( ) , b"fgh" ) ;
230+ assert_eq ! ( * cache. inner. num_fetches. lock( ) . await , 2 ) ;
231+
232+ // Should work even for fetch range larger than underlying buffer
233+ let result = cache. fetch ( 8 ..20 ) . await . unwrap ( ) ;
234+ assert_eq ! ( result. as_ref( ) , b"ijklmnopqrst" ) ;
235+ assert_eq ! ( * cache. inner. num_fetches. lock( ) . await , 3 ) ;
236+ }
237+ }
0 commit comments