22
33import asyncio
44import logging
5+ import time
56from typing import TYPE_CHECKING , Any , Literal
67
78from diracx .core .exceptions import SandboxAlreadyInsertedError , SandboxNotFoundError
@@ -221,8 +222,12 @@ async def clean_sandboxes(
221222 """
222223 total_deleted = 0
223224 cursor = 0
225+ batch_num = 0
224226 while True :
227+ batch_num += 1
228+
225229 # Phase 1: SELECT candidates (short transaction, no locks)
230+ t0 = time .monotonic ()
226231 async with sandbox_metadata_db :
227232 (
228233 sb_ids ,
@@ -232,25 +237,37 @@ async def clean_sandboxes(
232237 batch_size = batch_size ,
233238 cursor = cursor ,
234239 )
240+ select_duration = time .monotonic () - t0
235241
236242 if not pfns :
243+ logger .info (
244+ "Batch %d: no candidates found (%.1fs)" , batch_num , select_duration
245+ )
237246 break
238247
248+ logger .info (
249+ "Batch %d: selected %d candidates (%.1fs)" ,
250+ batch_num ,
251+ len (pfns ),
252+ select_duration ,
253+ )
254+
239255 # Phase 2: Delete from S3 (no transaction — prevents dark data
240256 # since S3 is cleaned first)
257+ t0 = time .monotonic ()
241258 objects : list [S3Object ] = [{"Key" : pfn_to_key (pfn )} for pfn in pfns ]
242- if logger .isEnabledFor (logging .DEBUG ):
243- for pfn in pfns :
244- logger .debug ("Deleting sandbox %s from S3" , pfn )
245-
246259 await s3_bulk_delete_with_retry (
247260 settings .s3_client , settings .bucket_name , objects
248261 )
249- logger .info ("Deleted %d sandboxes from %s" , len (objects ), settings .bucket_name )
262+ s3_duration = time .monotonic () - t0
263+ logger .info (
264+ "Batch %d: deleted %d from S3 (%.1fs)" , batch_num , len (objects ), s3_duration
265+ )
250266
251267 # Phase 3: Delete from DB in small chunks (each chunk is a short
252268 # transaction to avoid locking millions of rows in a single DELETE).
253269 # Up to 10 chunks run concurrently via a semaphore.
270+ t0 = time .monotonic ()
254271 delete_chunk_size = 1000
255272 sem = asyncio .Semaphore (10 )
256273
@@ -263,6 +280,15 @@ async def _delete_chunk(chunk: list[int], _sem: asyncio.Semaphore = sem) -> int:
263280 for i in range (0 , len (sb_ids ), delete_chunk_size )
264281 ]
265282 results = await asyncio .gather (* tasks )
266- total_deleted += sum (results )
283+ deleted = sum (results )
284+ db_duration = time .monotonic () - t0
285+ total_deleted += deleted
286+ logger .info (
287+ "Batch %d: deleted %d from DB (%.1fs, total so far: %d)" ,
288+ batch_num ,
289+ deleted ,
290+ db_duration ,
291+ total_deleted ,
292+ )
267293
268294 return total_deleted
0 commit comments