@@ -99,28 +99,28 @@ impl ImageDownloadHandler {
99
99
let mut conn = ctx. sql ( ) . await ?;
100
100
let mut tx = conn. begin ( ) . await ?;
101
101
102
- // Get total size of images directory. Note that it doesn't matter if this doesn't
103
- // match the actual fs size because it should either be exactly at or below actual fs
104
- // size. Also calculating fs size manually is expensive.
105
- let ( cache_count, images_dir_size) = sqlx:: query_as :: < _ , ( i64 , i64 ) > ( indoc ! (
106
- "
107
- SELECT COUNT(size), COALESCE(SUM(size), 0) FROM images_cache
108
- " ,
109
- ) )
110
- . fetch_one ( & mut * tx)
111
- . await
112
- . map_err ( Into :: < anyhow:: Error > :: into) ?;
102
+ let ( ( cache_count, images_dir_size) , image_download_size) = tokio:: try_join!(
103
+ async {
104
+ // Get total size of images directory. Note that it doesn't matter if this doesn't
105
+ // match the actual fs size because it should either be exactly at or below actual fs
106
+ // size. Also calculating fs size manually is expensive.
107
+ sqlx:: query_as:: <_, ( i64 , i64 ) >( indoc!(
108
+ "
109
+ SELECT COUNT(size), COALESCE(SUM(size), 0) FROM images_cache
110
+ " ,
111
+ ) )
112
+ . fetch_one( & mut * tx)
113
+ . await
114
+ . map_err( Into :: <anyhow:: Error >:: into)
115
+ } ,
116
+ // NOTE: The image size here is somewhat misleading because its only the size of the
117
+ // downloaded archive and not the total disk usage after it is unpacked. However, this is
118
+ // good enough
119
+ self . fetch_image_download_size( ctx, image_config) ,
120
+ ) ?;
113
121
114
122
// Prune images
115
- //
116
- // HACK: The artifact_size_bytes here is somewhat misleading because its only the size of the
117
- // downloaded archive and not the total disk usage after it is unpacked. However, this is size
118
- // is recalculated later once decompressed, so this will only ever exceed the cache
119
- // size limit in edge cases by `actual size - compressed size`. In this situation,
120
- // that extra difference is already reserved on the file system by the actor
121
- // itself.
122
- let ( removed_count, removed_bytes) = if images_dir_size as u64
123
- + image_config. artifact_size_bytes
123
+ let ( removed_count, removed_bytes) = if images_dir_size as u64 + image_download_size
124
124
> ctx. config ( ) . images . max_cache_size ( )
125
125
{
126
126
// Fetch as many images as it takes to clear up enough space for this new image.
@@ -157,7 +157,7 @@ impl ImageDownloadHandler {
157
157
. bind ( image_config. id )
158
158
. bind (
159
159
( images_dir_size as u64 )
160
- . saturating_add ( image_config . artifact_size_bytes )
160
+ . saturating_add ( image_download_size )
161
161
. saturating_sub ( ctx. config ( ) . images . max_cache_size ( ) ) as i64 ,
162
162
)
163
163
. fetch_all ( & mut * tx)
@@ -202,7 +202,7 @@ impl ImageDownloadHandler {
202
202
203
203
metrics:: IMAGE_CACHE_COUNT . set ( cache_count + 1 - removed_count) ;
204
204
metrics:: IMAGE_CACHE_SIZE
205
- . set ( images_dir_size + image_config . artifact_size_bytes as i64 - removed_bytes) ;
205
+ . set ( images_dir_size + image_download_size as i64 - removed_bytes) ;
206
206
207
207
sqlx:: query ( indoc ! (
208
208
"
@@ -230,7 +230,7 @@ impl ImageDownloadHandler {
230
230
metrics:: IMAGE_CACHE_SIZE . set ( images_dir_size + image_size as i64 - removed_bytes) ;
231
231
232
232
// Update state to signify download completed successfully
233
- sqlx:: query ( indoc ! (
233
+ let foo = sqlx:: query ( indoc ! (
234
234
"
235
235
UPDATE images_cache
236
236
SET
@@ -487,4 +487,51 @@ impl ImageDownloadHandler {
487
487
488
488
Ok ( addresses)
489
489
}
490
+
491
+ /// Attempts to fetch HEAD for the image download url and determine the image's download size.
492
+ async fn fetch_image_download_size (
493
+ & self ,
494
+ ctx : & Ctx ,
495
+ image_config : & protocol:: Image ,
496
+ ) -> Result < u64 > {
497
+ let addresses = self . get_image_addresses ( ctx, image_config) . await ?;
498
+
499
+ let mut iter = addresses. into_iter ( ) ;
500
+ while let Some ( artifact_url) = iter. next ( ) {
501
+ // Log the full URL we're attempting to download from
502
+ tracing:: info!( image_id=?image_config. id, %artifact_url, "attempting to download image" ) ;
503
+
504
+ match reqwest:: Client :: new ( )
505
+ . head ( & artifact_url)
506
+ . send ( )
507
+ . await
508
+ . and_then ( |res| res. error_for_status ( ) )
509
+ {
510
+ Ok ( res) => {
511
+ tracing:: info!( image_id=?image_config. id, %artifact_url, "successfully fetched image HEAD" ) ;
512
+
513
+ // Read Content-Length header from response
514
+ let image_size = res
515
+ . headers ( )
516
+ . get ( reqwest:: header:: CONTENT_LENGTH )
517
+ . context ( "no Content-Length header" ) ?
518
+ . to_str ( ) ?
519
+ . parse :: < u64 > ( )
520
+ . context ( "invalid Content-Length header" ) ?;
521
+
522
+ return Ok ( image_size) ;
523
+ }
524
+ Err ( err) => {
525
+ tracing:: warn!(
526
+ image_id=?image_config. id,
527
+ %artifact_url,
528
+ %err,
529
+ "failed to fetch image HEAD"
530
+ ) ;
531
+ }
532
+ }
533
+ }
534
+
535
+ bail ! ( "artifact url could not be resolved" ) ;
536
+ }
490
537
}
0 commit comments