@@ -116,15 +116,19 @@ impl HotTierManager {
116116 if let Some ( state) = self . state_cache . read ( ) . await . get ( & key) . cloned ( ) {
117117 return Ok ( state) ;
118118 }
119- let mut cache = self . state_cache . write ( ) . await ;
120- if let Some ( state) = cache. get ( & key) . cloned ( ) {
121- return Ok ( state) ;
122- }
119+ // key not present, reconcile
123120 let sht = self . reconcile_stream ( stream, tenant_id) . await ?;
124121 let state = Arc :: new ( StreamSyncState {
125122 sht : AsyncMutex :: new ( sht) ,
126123 } ) ;
127- cache. insert ( key, state. clone ( ) ) ;
124+
125+ let mut cache = self . state_cache . write ( ) . await ;
126+ if cache. insert ( key, state. clone ( ) ) . is_some ( ) {
127+ tracing:: warn!(
128+ "Key- {:?} was absent during read lock but already exists after reconcile!" ,
129+ ( tenant_id, stream) ,
130+ ) ;
131+ } ;
128132 Ok ( state)
129133 }
130134
@@ -221,31 +225,41 @@ impl HotTierManager {
221225 stream : & str ,
222226 tenant_id : & Option < String > ,
223227 ) -> Result < ( ) , HotTierError > {
224- let mut entries = fs:: read_dir ( date_dir) . await ?;
225- while let Some ( entry) = entries. next_entry ( ) . await ? {
226- let p = entry. path ( ) ;
227- let Some ( name_os) = p. file_name ( ) else {
228- continue ;
229- } ;
230- let name = name_os. to_string_lossy ( ) ;
231- if name. ends_with ( ".partial" ) {
232- let _ = fs:: remove_file ( & p) . await ;
233- * partials_removed += 1 ;
234- info ! (
235- stream = %stream,
236- tenant = ?tenant_id,
237- path = %p. display( ) ,
238- "reconcile: deleted partial orphan"
239- ) ;
240- continue ;
241- }
242- if name. ends_with ( ".manifest.json" ) {
243- continue ;
244- }
245- if !p. is_file ( ) {
246- continue ;
228+ let mut stack: Vec < PathBuf > = vec ! [ date_dir. clone( ) ] ;
229+ while let Some ( dir) = stack. pop ( ) {
230+ let mut entries = fs:: read_dir ( & dir) . await ?;
231+ while let Some ( entry) = entries. next_entry ( ) . await ? {
232+ let p = entry. path ( ) ;
233+ let ft = entry. file_type ( ) . await ?;
234+ if ft. is_dir ( ) {
235+ stack. push ( p) ;
236+ continue ;
237+ }
238+ let Some ( name_os) = p. file_name ( ) else {
239+ continue ;
240+ } ;
241+ let name = name_os. to_string_lossy ( ) ;
242+ if name. ends_with ( ".partial" ) {
243+ let _ = fs:: remove_file ( & p) . await ;
244+ * partials_removed += 1 ;
245+ info ! (
246+ stream = %stream,
247+ tenant = ?tenant_id,
248+ path = %p. display( ) ,
249+ "reconcile: deleted partial orphan"
250+ ) ;
251+ continue ;
252+ }
253+ if name. ends_with ( ".manifest.json" ) {
254+ continue ;
255+ }
256+ if !ft. is_file ( ) {
257+ continue ;
258+ }
259+ if let Ok ( rel) = p. strip_prefix ( date_dir) {
260+ on_disk. insert ( rel. to_string_lossy ( ) . into_owned ( ) ) ;
261+ }
247262 }
248- on_disk. insert ( name. into_owned ( ) ) ;
249263 }
250264 Ok ( ( ) )
251265 }
@@ -275,8 +289,8 @@ impl HotTierManager {
275289 Err ( _) => false ,
276290 } ;
277291 if ok {
278- if let Some ( name ) = local. file_name ( ) . and_then ( |s| s . to_str ( ) ) {
279- keep_names. insert ( name . to_owned ( ) ) ;
292+ if let Ok ( rel ) = local. strip_prefix ( date_dir ) {
293+ keep_names. insert ( rel . to_string_lossy ( ) . into_owned ( ) ) ;
280294 }
281295 * total_used += f. file_size ;
282296 kept. push ( f) ;
@@ -493,7 +507,7 @@ impl HotTierManager {
493507 ' a : ' static ,
494508 {
495509 let latest_min = PARSEABLE . options . hot_tier_latest_minutes ;
496- let historic_min = PARSEABLE . options . hot_tier_historic_sync_minutes . max ( 1 ) ;
510+ let historic_min = PARSEABLE . options . hot_tier_historic_sync_minutes ;
497511 info ! (
498512 latest_minutes = latest_min,
499513 historic_sync_minutes = historic_min,
@@ -516,6 +530,21 @@ impl HotTierManager {
516530 for stream in PARSEABLE . streams . list ( & tenant_id) {
517531 if this. check_stream_hot_tier_exists ( & stream, & tenant_id) {
518532 this. spawn_stream_tasks ( stream, tenant_id. clone ( ) ) . await ;
533+ } else {
534+ // check for potential orphan directory on disk
535+ let path = if let Some ( tenant_id) = tenant_id. as_ref ( ) {
536+ self . hot_tier_path . join ( tenant_id) . join ( stream)
537+ } else {
538+ self . hot_tier_path . join ( stream)
539+ } ;
540+ if path. exists ( ) {
541+ // delete this entire folder as stream meta says no hottier for stream
542+ if let Err ( e) = fs:: remove_dir_all ( & path) . await {
543+ tracing:: error!(
544+ "Unable to remove orphaned hottier dir- `{path:?}` with error- {e}"
545+ ) ;
546+ } ;
547+ }
519548 }
520549 }
521550 }
@@ -538,9 +567,8 @@ impl HotTierManager {
538567 }
539568
540569 let latest_interval = Duration :: from_secs ( 60 ) ;
541- let historic_interval = Duration :: from_secs (
542- PARSEABLE . options . hot_tier_historic_sync_minutes . max ( 1 ) as u64 * 60 ,
543- ) ;
570+ let historic_interval =
571+ Duration :: from_secs ( PARSEABLE . options . hot_tier_historic_sync_minutes as u64 * 60 ) ;
544572
545573 info ! ( stream = %stream, tenant = ?tenant_id, "spawning per-stream hot tier tasks" ) ;
546574
@@ -588,6 +616,12 @@ impl HotTierManager {
588616 if let Some ( t) = self . tasks . write ( ) . await . remove ( & key) {
589617 t. latest . abort ( ) ;
590618 t. historic . abort ( ) ;
619+
620+ // run these tasks till completion or till JoinError
621+ // post this, we delete the folders so its better if these tasks complete
622+ // and then deletion happens
623+ let _ = t. latest . await ;
624+ let _ = t. historic . await ;
591625 info ! ( stream = %stream, tenant = ?tenant_id, "aborted per-stream hot tier tasks" ) ;
592626 }
593627 }
@@ -658,8 +692,9 @@ impl HotTierManager {
658692 return Ok ( ( ) ) ;
659693 }
660694
661- let latest_minutes = PARSEABLE . options . hot_tier_latest_minutes . max ( 0 ) ;
662- let cutoff = chrono:: Utc :: now ( ) . naive_utc ( ) - chrono:: Duration :: minutes ( latest_minutes) ;
695+ let latest_minutes = PARSEABLE . options . hot_tier_latest_minutes ;
696+ let cutoff = chrono:: Utc :: now ( ) . naive_utc ( )
697+ - chrono:: Duration :: minutes ( latest_minutes. try_into ( ) . unwrap ( ) ) ;
663698
664699 // Build flat work list: (date, file, local_path) ordered latest-date-first,
665700 // and within a date by descending file_path (matches old `pop()` order).
@@ -717,10 +752,7 @@ impl HotTierManager {
717752 ) ;
718753
719754 let state = self . get_or_load_state ( stream, tenant_id) . await ?;
720- let concurrency = PARSEABLE
721- . options
722- . hot_tier_files_per_stream_concurrency
723- . max ( 4 ) ;
755+ let concurrency = PARSEABLE . options . hot_tier_files_per_stream_concurrency ;
724756
725757 // Reservation failure (out of disk + nothing to evict) is sticky:
726758 // once one file can't be placed, no subsequent file will fit either.
@@ -896,7 +928,7 @@ impl HotTierManager {
896928 let download_result = PARSEABLE
897929 . storage
898930 . get_object_store ( )
899- . buffered_write ( & parquet_file_path, tenant_id, parquet_path. clone ( ) )
931+ . parallel_chunked_download ( & parquet_file_path, tenant_id, parquet_path. clone ( ) )
900932 . await ;
901933 let dl_elapsed = dl_start. elapsed ( ) ;
902934
@@ -1150,9 +1182,23 @@ impl HotTierManager {
11501182 let mut delete_successful = false ;
11511183 let mut freed_total: u64 = 0 ;
11521184 let dates = self . fetch_hot_tier_dates ( stream, tenant_id) . await ?;
1185+ if dates. is_empty ( ) {
1186+ info ! (
1187+ stream = %stream,
1188+ tenant = ?tenant_id,
1189+ "eviction: no date dirs found, nothing to evict"
1190+ ) ;
1191+ }
11531192 ' loop_dates: for date in dates {
11541193 let path = self . get_stream_path_for_date ( stream, & date, tenant_id) ;
11551194 if !path. exists ( ) {
1195+ info ! (
1196+ stream = %stream,
1197+ tenant = ?tenant_id,
1198+ date = %date,
1199+ path = %path. display( ) ,
1200+ "eviction: date path missing, skipping"
1201+ ) ;
11561202 continue ;
11571203 }
11581204
@@ -1164,11 +1210,27 @@ impl HotTierManager {
11641210 . to_string_lossy ( )
11651211 . ends_with ( ".manifest.json" )
11661212 } ) ;
1213+ if manifest_files. is_empty ( ) {
1214+ info ! (
1215+ stream = %stream,
1216+ tenant = ?tenant_id,
1217+ date = %date,
1218+ path = %path. display( ) ,
1219+ "eviction: no .manifest.json files in date dir"
1220+ ) ;
1221+ continue ;
1222+ }
11671223 for manifest_file in manifest_files {
11681224 let file = fs:: read ( manifest_file. path ( ) ) . await ?;
11691225 let mut manifest: Manifest = serde_json:: from_slice ( & file) ?;
11701226
11711227 if manifest. files . is_empty ( ) {
1228+ info ! (
1229+ stream = %stream,
1230+ tenant = ?tenant_id,
1231+ manifest = %manifest_file. path( ) . display( ) ,
1232+ "eviction: manifest has zero file entries"
1233+ ) ;
11721234 continue ;
11731235 }
11741236
@@ -1182,7 +1244,18 @@ impl HotTierManager {
11821244 let first_file_path = self . hot_tier_path . join ( & first_file. file_path ) ;
11831245 let minute_to_delete = first_file_path. parent ( ) . unwrap ( ) ;
11841246
1185- if minute_to_delete. exists ( ) {
1247+ if !minute_to_delete. exists ( ) {
1248+ info ! (
1249+ stream = %stream,
1250+ tenant = ?tenant_id,
1251+ manifest = %manifest_file. path( ) . display( ) ,
1252+ first_file = %first_file. file_path,
1253+ minute = %minute_to_delete. display( ) ,
1254+ "eviction: minute dir referenced by manifest does not exist on disk"
1255+ ) ;
1256+ continue ;
1257+ }
1258+ {
11861259 if let ( Some ( download_date_time) , Some ( delete_date_time) ) = (
11871260 extract_datetime ( download_file_path. to_str ( ) . unwrap ( ) ) ,
11881261 extract_datetime ( first_file_path. to_str ( ) . unwrap ( ) ) ,
@@ -1244,7 +1317,7 @@ impl HotTierManager {
12441317 new_available = stream_hot_tier. available_size,
12451318 "evicted"
12461319 ) ;
1247- if stream_hot_tier. available_size <= parquet_file_size {
1320+ if stream_hot_tier. available_size < parquet_file_size {
12481321 continue ;
12491322 } else {
12501323 break ' loop_dates;
@@ -1263,7 +1336,7 @@ impl HotTierManager {
12631336 Ok ( delete_successful)
12641337 }
12651338
1266- ///check if the disk is available to download the parquet file
1339+ /// check if the disk is available to download the parquet file
12671340 /// check if the disk usage is above the threshold
12681341 pub async fn is_disk_available ( & self , size_to_download : u64 ) -> Result < bool , HotTierError > {
12691342 if let Some ( DiskUtil {
0 commit comments