Skip to content

Commit 31157e6

Browse files
chrisburraldbr
authored andcommitted
fix: chunk DB deletes into 1000-row batches with concurrent execution
A single DELETE of 50k rows caused MySQL to lock 30M rows during the index scan. Split into 1000-row chunks running up to 10 concurrently via asyncio.Semaphore, keeping each transaction short.
1 parent 8af56f2 commit 31157e6

1 file changed

Lines changed: 17 additions & 4 deletions

File tree

diracx-logic/src/diracx/logic/jobs/sandboxes.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import asyncio
34
import logging
45
from typing import TYPE_CHECKING, Any, Literal
56

@@ -247,9 +248,21 @@ async def clean_sandboxes(
247248
)
248249
logger.info("Deleted %d sandboxes from %s", len(objects), settings.bucket_name)
249250

250-
# Phase 3: Delete from DB (short transaction)
251-
async with sandbox_metadata_db:
252-
await sandbox_metadata_db.delete_sandboxes(sb_ids)
253-
total_deleted += len(sb_ids)
251+
# Phase 3: Delete from DB in small chunks (each chunk is a short
252+
# transaction to avoid locking millions of rows in a single DELETE).
253+
# Up to 10 chunks run concurrently via a semaphore.
254+
delete_chunk_size = 1000
255+
sem = asyncio.Semaphore(10)
256+
257+
async def _delete_chunk(chunk: list[int], _sem: asyncio.Semaphore = sem) -> int:
258+
async with _sem, sandbox_metadata_db:
259+
return await sandbox_metadata_db.delete_sandboxes(chunk)
260+
261+
tasks = [
262+
_delete_chunk(sb_ids[i : i + delete_chunk_size])
263+
for i in range(0, len(sb_ids), delete_chunk_size)
264+
]
265+
results = await asyncio.gather(*tasks)
266+
total_deleted += sum(results)
254267

255268
return total_deleted

0 commit comments

Comments
 (0)