@@ -108,6 +108,16 @@ async def _has_link_references(self, session_id: str, file_id: str) -> bool:
108108 links_key = self ._get_file_links_key (session_id , file_id )
109109 return bool (await self .redis_client .smembers (links_key ))
110110
111+ async def _delete_object (self , object_key : str ) -> None :
112+ """Delete a backing object from MinIO."""
113+ loop = asyncio .get_event_loop ()
114+ await loop .run_in_executor (
115+ None ,
116+ self .minio_client .remove_object ,
117+ self .bucket_name ,
118+ object_key ,
119+ )
120+
111121 async def _find_linked_file (
112122 self , target_session_id : str , source_session_id : str , source_file_id : str
113123 ) -> Optional [str ]:
@@ -173,7 +183,7 @@ async def get_file_metadata(
173183 # Convert string values back to appropriate types
174184 if "size" in metadata :
175185 metadata ["size" ] = int (metadata ["size" ])
176- if "created_at" in metadata :
186+ if "created_at" in metadata and isinstance ( metadata [ "created_at" ], str ) :
177187 metadata ["created_at" ] = datetime .fromisoformat (metadata ["created_at" ])
178188
179189 return metadata
@@ -485,6 +495,31 @@ async def delete_file(self, session_id: str, file_id: str) -> bool:
485495 session_id = session_id ,
486496 file_id = file_id ,
487497 )
498+
499+ source_metadata = await self .get_file_metadata (
500+ metadata ["source_session_id" ],
501+ metadata ["source_file_id" ],
502+ )
503+ if source_metadata is None and not await self ._has_link_references (
504+ metadata ["source_session_id" ],
505+ metadata ["source_file_id" ],
506+ ):
507+ try :
508+ await self ._delete_object (metadata ["object_key" ])
509+ logger .debug (
510+ "Deleted orphaned shared object after final alias cleanup" ,
511+ source_session_id = metadata ["source_session_id" ],
512+ source_file_id = metadata ["source_file_id" ],
513+ object_key = metadata ["object_key" ],
514+ )
515+ except S3Error as e :
516+ logger .warning (
517+ "Failed to delete orphaned shared object" ,
518+ source_session_id = metadata ["source_session_id" ],
519+ source_file_id = metadata ["source_file_id" ],
520+ object_key = metadata ["object_key" ],
521+ error = str (e ),
522+ )
488523 return True
489524
490525 if await self ._has_link_references (session_id , file_id ):
@@ -500,10 +535,7 @@ async def delete_file(self, session_id: str, file_id: str) -> bool:
500535
501536 try :
502537 # Delete from MinIO
503- loop = asyncio .get_event_loop ()
504- await loop .run_in_executor (
505- None , self .minio_client .remove_object , self .bucket_name , object_key
506- )
538+ await self ._delete_object (object_key )
507539
508540 # Delete metadata from Redis
509541 await self ._delete_file_metadata (session_id , file_id )
0 commit comments