Skip to content

Commit 41e8ace

Browse files
authored
Increasing pruning rate with threads (#73010)
Job runs are timing out at 8h. Too many scos tags are being created, but that is a separate mystery. For now, try to catch up with faster pruning.
1 parent eb6c253 commit 41e8ace

1 file changed

Lines changed: 42 additions & 29 deletions

File tree

hack/qci_registry_pruner.py

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import logging
7575
from typing import Optional, Set, Dict, List
7676
from datetime import datetime
77+
from concurrent.futures import ThreadPoolExecutor
7778

7879
QUAY_OAUTH_TOKEN_ENV_NAME = "QUAY_OAUTH_TOKEN"
7980

@@ -134,8 +135,8 @@ def create_tag(repository: str, tag: str, manifest_digest: str, token: str):
134135
return False
135136

136137

137-
def delete_tag(repository: str, tag: str, token: str):
138-
"""Delete a tag from a quay.io repository"""
138+
def delete_tag(repository: str, tag: str, token: str) -> bool:
139+
"""Delete a tag from a quay.io repository. Returns True on success, False on failure."""
139140
delete_url = f"https://quay.io/api/v1/repository/{repository}/tag/{tag}"
140141
headers = {
141142
"Authorization": f"Bearer {token}"
@@ -148,10 +149,14 @@ def delete_tag(repository: str, tag: str, token: str):
148149
try:
149150
with urllib.request.urlopen(request) as response:
150151
response_data = response.read()
151-
if response.status != 204:
152-
logging.error("Failed to delete tag '%s': %d %s", tag, response.status, response_data)
152+
if response.status == 204:
153+
logging.info('Successfully deleted %s', tag)
154+
return True
155+
logging.error("Failed to delete tag '%s': %d %s", tag, response.status, response_data)
156+
return False
153157
except Exception: # pylint: disable=broad-except
154158
logging.exception('Failed to delete tag "%s"', tag)
159+
return False
155160

156161

157162
def fetch_tags(repository: str, token: str, page: int = 1, like: Optional[str] = None):
@@ -318,7 +323,6 @@ def run(args, start_time): # pylint: disable=too-many-statements,redefined-oute
318323
has_more = True
319324

320325
prune_target_tags = set()
321-
pruned_tags = set()
322326
tag_count = 0
323327
mod_by = 5
324328

@@ -328,6 +332,10 @@ def run(args, start_time): # pylint: disable=too-many-statements,redefined-oute
328332
remove_requests: Set[str] = set() # versions to remove
329333
component_tags: Dict[str, List[str]] = {} # version -> list of component tag names
330334

335+
# Executor for concurrent tag deletions (up to 100 simultaneous requests)
336+
delete_executor = ThreadPoolExecutor(max_workers=100)
337+
delete_futures = [] # List of futures
338+
331339
while has_more:
332340
retries = 5
333341
while True:
@@ -394,17 +402,21 @@ def run(args, start_time): # pylint: disable=too-many-statements,redefined-oute
394402
if days_difference > ttl_days and image_tag not in prune_target_tags:
395403
prune_target_tags.add(image_tag)
396404
if confirm:
397-
try:
398-
delete_tag(QUAY_CI_REPO, tag=image_tag, token=token)
399-
logging.debug('Removed %s', image_tag)
400-
pruned_tags.add(image_tag)
401-
except Exception: # pylint: disable=broad-except
402-
logging.exception('Error while trying to delete tag %s', image_tag)
405+
delete_futures.append(delete_executor.submit(delete_tag, QUAY_CI_REPO, image_tag, token))
403406
else:
404407
logging.debug('Would have removed %s', image_tag)
405408

406409
page += 1
407410

411+
# Wait for all prune delete operations to complete
412+
prune_success_count = 0
413+
if delete_futures:
414+
logging.info('Waiting for %d prune delete operations to complete...', len(delete_futures))
415+
for future in delete_futures:
416+
if future.result(): # Returns True on success
417+
prune_success_count += 1
418+
delete_futures.clear()
419+
408420
# Process release payload preservation
409421
logging.info("Processing release payload preservation...")
410422
logging.info("Found %d rc_payload__ tags", len(rc_payload_tags))
@@ -429,8 +441,8 @@ def run(args, start_time): # pylint: disable=too-many-statements,redefined-oute
429441

430442
# Process removal requests
431443
removal_requests_processed = 0
432-
removal_tags_removed = 0
433444
removal_tags_target = 0
445+
removal_success_count = 0
434446

435447
if remove_requests:
436448
logging.info("Processing %d removal requests...", len(remove_requests))
@@ -461,12 +473,7 @@ def run(args, start_time): # pylint: disable=too-many-statements,redefined-oute
461473
# Remove all related tags
462474
if confirm:
463475
for tag_to_remove in tags_to_remove:
464-
try:
465-
delete_tag(QUAY_CI_REPO, tag_to_remove, token)
466-
logging.info("Removed tag: %s", tag_to_remove)
467-
removal_tags_removed += 1
468-
except Exception: # pylint: disable=broad-except
469-
logging.exception("Error removing tag %s", tag_to_remove)
476+
delete_futures.append(delete_executor.submit(delete_tag, QUAY_CI_REPO, tag_to_remove, token))
470477
else:
471478
for tag_to_remove in tags_to_remove:
472479
logging.info("Would remove tag: %s", tag_to_remove)
@@ -477,30 +484,36 @@ def run(args, start_time): # pylint: disable=too-many-statements,redefined-oute
477484
removal_tags_target += 1
478485

479486
if confirm:
480-
try:
481-
delete_tag(QUAY_CI_REPO, remove_tag, token)
482-
logging.info("Removed removal request tag: %s", remove_tag)
483-
removal_tags_removed += 1
484-
except Exception: # pylint: disable=broad-except
485-
logging.exception("Error removing request tag %s", remove_tag)
487+
delete_futures.append(delete_executor.submit(delete_tag, QUAY_CI_REPO, remove_tag, token))
486488
else:
487489
logging.info("Would remove request tag: %s", remove_tag)
488490

491+
# Wait for all removal delete operations to complete
492+
if delete_futures:
493+
logging.info('Waiting for %d removal delete operations to complete...', len(delete_futures))
494+
for future in delete_futures:
495+
if future.result(): # Returns True on success
496+
removal_success_count += 1
497+
delete_futures.clear()
498+
489499
finish_time = datetime.now()
490500
logging.info('Duration: %s', finish_time - start_time)
491501
logging.info('Total tags scanned: %d', tag_count)
492-
logging.info('Tags pruned (if --confirm): %d', len(prune_target_tags))
493-
logging.info('Tags actually pruned: %d', len(pruned_tags))
502+
logging.info('Tags targeted for pruning: %d', len(prune_target_tags))
503+
if confirm:
504+
logging.info('Tags successfully pruned: %d', prune_success_count)
494505
logging.info('Release payloads needing preservation: %d', payloads_needing_preservation)
495506
if confirm:
496507
logging.info('Release payloads preserved: %d', payloads_preserved)
497508
else:
498509
logging.info('Release payloads that would be preserved: %d', payloads_needing_preservation)
499510
logging.info('Removal requests processed: %d', removal_requests_processed)
511+
logging.info('Release payload tags targeted for removal: %d', removal_tags_target)
500512
if confirm:
501-
logging.info('Release payload tags removed: %d', removal_tags_removed)
502-
else:
503-
logging.info('Release payload tags that would be removed: %d', removal_tags_target)
513+
logging.info('Release payload tags successfully removed: %d', removal_success_count)
514+
515+
# Cleanup executor
516+
delete_executor.shutdown(wait=False)
504517

505518
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%dT%H:%M:%S')
506519

0 commit comments

Comments
 (0)