@@ -34,7 +34,6 @@ use crate::{
3434 validator:: error:: HotTierValidationError ,
3535} ;
3636use chrono:: NaiveDate ;
37- use clokwerk:: { AsyncScheduler , Interval , Job } ;
3837use futures:: { StreamExt , TryStreamExt , stream:: FuturesUnordered } ;
3938use futures_util:: TryFutureExt ;
4039use object_store:: { ObjectStoreExt , local:: LocalFileSystem } ;
@@ -49,7 +48,6 @@ use tracing::{error, warn};
4948
5049pub const STREAM_HOT_TIER_FILENAME : & str = ".hot_tier.json" ;
5150pub const MIN_STREAM_HOT_TIER_SIZE_BYTES : u64 = 10737418240 ; // 10 GiB
52- const HOT_TIER_SYNC_DURATION : Interval = clokwerk:: Interval :: Minutes ( 1 ) ;
5351pub const INTERNAL_STREAM_HOT_TIER_SIZE_BYTES : u64 = 10485760 ; //10 MiB
5452pub const CURRENT_HOT_TIER_VERSION : & str = "v2" ;
5553
@@ -436,10 +434,11 @@ impl HotTierManager {
436434 Ok ( path)
437435 }
438436
439- /// schedule two hot-tier sync jobs: a frequent "latest" pass for files
440- /// within the last `P_HOT_TIER_LATEST_MINUTES`, and a less-frequent
441- /// "historic" pass for older files. Both share the per-stream lock; only
442- /// the latest pass triggers eviction.
437+ /// Spawn two independent loops, one per phase, so Latest and Historic
438+ /// tick clocks are independent. A long-running Historic backfill no
439+ /// longer delays Latest ticks. Within each phase, the next iteration
440+ /// starts only after the previous returns + a sleep, so a phase can
441+ /// never overlap itself.
443442 pub fn download_from_s3 < ' a > ( & ' a self ) -> Result < ( ) , HotTierError >
444443 where
445444 ' a : ' static ,
@@ -452,31 +451,28 @@ impl HotTierManager {
452451 "hot tier scheduler starting"
453452 ) ;
454453
455- let mut scheduler = AsyncScheduler :: new ( ) ;
456- scheduler
457- . every ( HOT_TIER_SYNC_DURATION )
458- . plus ( Interval :: Seconds ( 5 ) )
459- . run ( move || async {
454+ let latest_interval = Duration :: from_secs ( 60 ) ;
455+ let historic_interval = Duration :: from_secs ( historic_min as u64 * 60 ) ;
456+
457+ let this = self ;
458+ tokio:: spawn ( async move {
459+ loop {
460460 warn ! ( phase = ?SyncPhase :: Latest , "hot tier tick fired" ) ;
461- if let Err ( err) = self . sync_hot_tier ( SyncPhase :: Latest ) . await {
461+ if let Err ( err) = this . sync_hot_tier ( SyncPhase :: Latest ) . await {
462462 error ! ( "Error in hot tier latest scheduler: {:?}" , err) ;
463463 }
464- } ) ;
464+ tokio:: time:: sleep ( latest_interval) . await ;
465+ }
466+ } ) ;
465467
466- scheduler
467- . every ( Interval :: Minutes ( historic_min) )
468- . plus ( Interval :: Seconds ( 15 ) )
469- . run ( move || async {
468+ let this = self ;
469+ tokio:: spawn ( async move {
470+ loop {
470471 warn ! ( phase = ?SyncPhase :: Historic , "hot tier tick fired" ) ;
471- if let Err ( err) = self . sync_hot_tier ( SyncPhase :: Historic ) . await {
472+ if let Err ( err) = this . sync_hot_tier ( SyncPhase :: Historic ) . await {
472473 error ! ( "Error in hot tier historic scheduler: {:?}" , err) ;
473474 }
474- } ) ;
475-
476- tokio:: spawn ( async move {
477- loop {
478- scheduler. run_pending ( ) . await ;
479- tokio:: time:: sleep ( Duration :: from_secs ( 10 ) ) . await ;
475+ tokio:: time:: sleep ( historic_interval) . await ;
480476 }
481477 } ) ;
482478 Ok ( ( ) )
@@ -510,13 +506,18 @@ impl HotTierManager {
510506 }
511507 warn ! ( phase = ?phase, num_streams = scheduled, "hot tier tick: streams scheduled" ) ;
512508
509+ let tick_start = std:: time:: Instant :: now ( ) ;
513510 while let Some ( res) = sync_hot_tier_tasks. next ( ) . await {
514511 if let Err ( err) = res {
515512 error ! ( "Failed to run hot tier sync task {err:?}" ) ;
516513 return Err ( err) ;
517514 }
518515 }
519- warn ! ( phase = ?phase, "hot tier tick complete" ) ;
516+ warn ! (
517+ phase = ?phase,
518+ elapsed_ms = tick_start. elapsed( ) . as_millis( ) as u64 ,
519+ "hot tier tick complete"
520+ ) ;
520521 Ok ( ( ) )
521522 }
522523
@@ -557,10 +558,17 @@ impl HotTierManager {
557558 "manifest fetched"
558559 ) ;
559560
561+ let stream_start = std:: time:: Instant :: now ( ) ;
560562 self . process_manifest ( & stream, & mut s3_manifest_file_list, & tenant_id, phase)
561563 . await ?;
562564
563- warn ! ( stream = %stream, tenant = ?tenant_id, phase = ?phase, "stream sync done" ) ;
565+ warn ! (
566+ stream = %stream,
567+ tenant = ?tenant_id,
568+ phase = ?phase,
569+ elapsed_ms = stream_start. elapsed( ) . as_millis( ) as u64 ,
570+ "stream sync done"
571+ ) ;
564572 Ok ( ( ) )
565573 }
566574
@@ -671,15 +679,14 @@ impl HotTierManager {
671679 phase,
672680 )
673681 . await ?;
674- if !processed
675- && !stop. swap ( true , std:: sync:: atomic:: Ordering :: Relaxed ) {
676- warn ! (
677- stream = %stream,
678- tenant = ?tenant_id,
679- phase = ?phase,
680- "sticky stop: halting further reservations this tick"
681- ) ;
682- }
682+ if !processed && !stop. swap ( true , std:: sync:: atomic:: Ordering :: Relaxed ) {
683+ warn ! (
684+ stream = %stream,
685+ tenant = ?tenant_id,
686+ phase = ?phase,
687+ "sticky stop: halting further reservations this tick"
688+ ) ;
689+ }
683690 Ok ( ( ) )
684691 }
685692 } )
@@ -801,17 +808,20 @@ impl HotTierManager {
801808 file_size = parquet_file. file_size,
802809 "download starting"
803810 ) ;
811+ let dl_start = std:: time:: Instant :: now ( ) ;
804812 let download_result = PARSEABLE
805813 . storage
806814 . get_object_store ( )
807815 . buffered_write ( & parquet_file_path, tenant_id, parquet_path. clone ( ) )
808816 . await ;
817+ let dl_elapsed = dl_start. elapsed ( ) ;
809818
810819 if let Err ( e) = download_result {
811820 warn ! (
812821 stream = %stream,
813822 tenant = ?tenant_id,
814823 file = %parquet_file. file_path,
824+ elapsed_ms = dl_elapsed. as_millis( ) as u64 ,
815825 err = %e,
816826 "download failed, refunding reservation"
817827 ) ;
@@ -824,11 +834,19 @@ impl HotTierManager {
824834 // backend already cleaned up its `.partial` file; final path was never created.
825835 return Err ( e. into ( ) ) ;
826836 }
837+ let elapsed_ms = dl_elapsed. as_millis ( ) as u64 ;
838+ let mbps = if dl_elapsed. as_secs_f64 ( ) > 0.0 {
839+ ( parquet_file. file_size as f64 * 8.0 ) / dl_elapsed. as_secs_f64 ( ) / 1_000_000.0
840+ } else {
841+ 0.0
842+ } ;
827843 warn ! (
828844 stream = %stream,
829845 tenant = ?tenant_id,
830846 file = %parquet_file. file_path,
831847 file_size = parquet_file. file_size,
848+ elapsed_ms,
849+ mbps = format!( "{mbps:.1}" ) ,
832850 "download finished, committing"
833851 ) ;
834852
0 commit comments