@@ -151,7 +151,7 @@ async fn http_cache_put(context: &Context, url: &str, response: &Response) -> Re
151
151
/// Also returns if the response is stale and should be revalidated in the background.
152
152
async fn http_cache_get ( context : & Context , url : & str ) -> Result < Option < ( Response , bool ) > > {
153
153
let now = time ( ) ;
154
- let Some ( ( blob_name, mimetype, encoding, is_stale ) ) = context
154
+ let Some ( ( blob_name, mimetype, encoding, stale_timestamp ) ) = context
155
155
. sql
156
156
. query_row_optional (
157
157
"SELECT blobname, mimetype, encoding, stale
@@ -162,13 +162,14 @@ async fn http_cache_get(context: &Context, url: &str) -> Result<Option<(Response
162
162
let mimetype: Option < String > = Some ( row. get ( 1 ) ?) . filter ( |s : & String | !s. is_empty ( ) ) ;
163
163
let encoding: Option < String > = Some ( row. get ( 2 ) ?) . filter ( |s : & String | !s. is_empty ( ) ) ;
164
164
let stale_timestamp: i64 = row. get ( 3 ) ?;
165
- Ok ( ( blob_name, mimetype, encoding, now > stale_timestamp) )
165
+ Ok ( ( blob_name, mimetype, encoding, stale_timestamp) )
166
166
} ,
167
167
)
168
168
. await ?
169
169
else {
170
170
return Ok ( None ) ;
171
171
} ;
172
+ let is_stale = now > stale_timestamp;
172
173
173
174
let blob_object = BlobObject :: from_name ( context, blob_name) ?;
174
175
let blob_abs_path = blob_object. to_abs_path ( ) ;
@@ -195,15 +196,16 @@ async fn http_cache_get(context: &Context, url: &str) -> Result<Option<(Response
195
196
// Update expiration timestamp
196
197
// to prevent deletion of the file still in use.
197
198
//
198
- // We do not update stale timestamp here
199
- // as we have not revalidated the response.
200
- // Stale timestamp is updated only
201
- // when the URL is sucessfully fetched.
199
+ // If the response is stale, the caller should revalidate it in the background, so update
200
+ // `stale` timestamp to avoid revalidating too frequently (and have many parallel revalidation
201
+ // tasks) if revalidation fails or the HTTP request takes some time. The stale period >= 1 hour,
202
+ // so 1 more minute won't be a problem.
203
+ let stale_timestamp = if is_stale { now + 60 } else { stale_timestamp } ;
202
204
context
203
205
. sql
204
206
. execute (
205
- "UPDATE http_cache SET expires=? WHERE url=?" ,
206
- ( expires, url) ,
207
+ "UPDATE http_cache SET expires=?, stale=? WHERE url=?" ,
208
+ ( expires, stale_timestamp , url) ,
207
209
)
208
210
. await ?;
209
211
@@ -305,8 +307,6 @@ pub async fn read_url_blob(context: &Context, url: &str) -> Result<Response> {
305
307
}
306
308
} ) ;
307
309
}
308
-
309
- // Return stale result.
310
310
return Ok ( response) ;
311
311
}
312
312
@@ -495,6 +495,22 @@ mod tests {
495
495
) ;
496
496
assert_eq ! ( http_cache_get( t, xdc_pixel_url) . await ?, None ) ;
497
497
498
+ // If we get the blob the second time quickly, it shouldn't be stale because it's supposed
499
+ // that we've already run a revalidation task which will update the blob soon.
500
+ assert_eq ! (
501
+ http_cache_get( t, xdc_editor_url) . await ?,
502
+ Some ( ( xdc_response. clone( ) , false ) )
503
+ ) ;
504
+ // But if the revalidation task hasn't succeeded after some time, the blob is stale again
505
+ // even if we continue to get it frequently.
506
+ for i in ( 0 ..100 ) . rev ( ) {
507
+ SystemTime :: shift ( Duration :: from_secs ( 6 ) ) ;
508
+ if let Some ( ( _, true ) ) = http_cache_get ( t, xdc_editor_url) . await ? {
509
+ break ;
510
+ }
511
+ assert ! ( i > 0 ) ;
512
+ }
513
+
498
514
// Test that if the file is accidentally removed from the blobdir,
499
515
// there is no error when trying to load the cache entry.
500
516
for entry in std:: fs:: read_dir ( t. get_blobdir ( ) ) ? {
0 commit comments