11use crate :: AppState ;
22use anyhow:: Context ;
33use async_trait:: async_trait;
4+ use chrono:: { DateTime , TimeZone , Utc } ;
45use dashmap:: DashMap ;
56use std:: path:: PathBuf ;
67use std:: sync:: atomic:: {
78 AtomicU64 ,
89 Ordering :: { Acquire , Release } ,
910} ;
1011use std:: sync:: Arc ;
11- use std:: time:: { Duration , SystemTime } ;
12+ use std:: time:: SystemTime ;
1213
1314const MAX_STALE_CACHE_MS : u64 = 100 ;
1415
@@ -33,25 +34,30 @@ impl<T> Cached<T> {
3334 this. last_checked_at . store ( u64:: MAX , Release ) ;
3435 this
3536 }
36- fn last_check_time ( & self ) -> SystemTime {
37- let millis = self
38- . last_checked_at
37+ fn last_check_time ( & self ) -> DateTime < Utc > {
38+ self . last_checked_at
3939 . load ( Acquire )
40- . saturating_mul ( MAX_STALE_CACHE_MS ) ;
41- SystemTime :: UNIX_EPOCH + Duration :: from_millis ( millis)
40+ . saturating_mul ( MAX_STALE_CACHE_MS )
41+ . try_into ( )
42+ . ok ( )
43+ . and_then ( |millis| Utc . timestamp_millis_opt ( millis) . single ( ) )
44+ . expect ( "file timestamp out of bound" )
4245 }
4346 fn update_check_time ( & self ) {
44- let elapsed = u64:: try_from ( Self :: elapsed ( ) ) . expect ( "too far in the future" ) ;
45- self . last_checked_at . store ( elapsed, Release ) ;
47+ self . last_checked_at . store ( Self :: elapsed ( ) , Release ) ;
4648 }
47- fn elapsed ( ) -> u128 {
48- ( SystemTime :: now ( ) . duration_since ( SystemTime :: UNIX_EPOCH ) )
49- . unwrap ( )
50- . as_millis ( )
51- / u128:: from ( MAX_STALE_CACHE_MS )
49+ fn elapsed ( ) -> u64 {
50+ let timestamp_millis = ( SystemTime :: now ( ) . duration_since ( SystemTime :: UNIX_EPOCH ) )
51+ . expect ( "invalid duration" )
52+ . as_millis ( ) ;
53+ let elapsed_intervals = timestamp_millis / u128:: from ( MAX_STALE_CACHE_MS ) ;
54+ u64:: try_from ( elapsed_intervals) . expect ( "invalid date" )
5255 }
5356 fn needs_check ( & self ) -> bool {
54- self . last_check_time ( ) + Duration :: from_millis ( MAX_STALE_CACHE_MS ) <= SystemTime :: now ( )
57+ self . last_checked_at
58+ . load ( Acquire )
59+ . saturating_add ( MAX_STALE_CACHE_MS )
60+ < Self :: elapsed ( )
5561 }
5662}
5763
@@ -79,24 +85,25 @@ impl<T: AsyncFromStrWithState> FileCache<T> {
7985 log:: trace!( "Cache answer without filesystem lookup for {:?}" , path) ;
8086 return Ok ( Arc :: clone ( & cached. content ) ) ;
8187 }
82- let modified_res = tokio:: fs:: metadata ( path) . await . and_then ( |m| m. modified ( ) ) ;
83- match modified_res {
84- Ok ( modified) => {
85- if modified <= cached. last_check_time ( ) {
86- log:: trace!( "Cache answer with filesystem metadata read for {:?}" , path) ;
87- cached. update_check_time ( ) ;
88- return Ok ( Arc :: clone ( & cached. content ) ) ;
89- }
88+ match app_state
89+ . file_system
90+ . modified_since ( app_state, path, cached. last_check_time ( ) )
91+ . await
92+ {
93+ Ok ( false ) => {
94+ log:: trace!( "Cache answer with filesystem metadata read for {:?}" , path) ;
95+ cached. update_check_time ( ) ;
96+ return Ok ( Arc :: clone ( & cached. content ) ) ;
9097 }
91- Err ( e) => log:: warn!(
92- "Unable to check when '{}' was last modified. Re-reading the file: {e:#}" ,
93- path. display( )
94- ) ,
98+ Ok ( true ) => log:: trace!( "{path:?} was changed, updating cache..." ) ,
99+ Err ( e) => log:: warn!( "Cannot read metadata of {path:?}, re-loading it: {e:#}" ) ,
95100 }
96101 }
97102 // Read lock is released
98103 log:: trace!( "Loading and parsing {:?}" , path) ;
99- let file_contents = tokio:: fs:: read_to_string ( path)
104+ let file_contents = app_state
105+ . file_system
106+ . read_file ( app_state, path)
100107 . await
101108 . with_context ( || format ! ( "Reading {path:?} to load it in cache" ) ) ;
102109 let parsed = match file_contents {
0 commit comments