mirror of
https://github.com/ansible/awx.git
synced 2026-07-24 14:48:17 -02:30
fix: delete inventory hosts in batches to avoid memory exhaustion
This commit is contained in:
@@ -1022,6 +1022,33 @@ def update_host_smart_inventory_memberships():
|
|||||||
smart_inventory.update_computed_fields()
|
smart_inventory.update_computed_fields()
|
||||||
|
|
||||||
|
|
||||||
|
def _batched_delete_inventory(inventory, batch_size=500):
|
||||||
|
"""Delete inventory hosts in batches to avoid high memory usage.
|
||||||
|
|
||||||
|
With ansible facts, loading thousands of hosts at once can use a lot of memory. To avoid
|
||||||
|
this, we delete them in batches (of 500).
|
||||||
|
|
||||||
|
Safe to retry after a crash because inventory.pending_deletion
|
||||||
|
is already set and each batch is its own transaction.
|
||||||
|
"""
|
||||||
|
from awx.main.models.inventory import Host
|
||||||
|
|
||||||
|
# first delete all hosts in batches
|
||||||
|
total_deleted = 0
|
||||||
|
while True:
|
||||||
|
pks = list(Host.objects.filter(inventory_id=inventory.id).values_list('pk', flat=True)[:batch_size])
|
||||||
|
if not pks:
|
||||||
|
break
|
||||||
|
with transaction.atomic():
|
||||||
|
deleted_count, _ = Host.objects.filter(pk__in=pks).delete()
|
||||||
|
total_deleted += deleted_count
|
||||||
|
logger.debug('Batch-deleted %d hosts from inventory %d (%d total so far)', len(pks), inventory.id, total_deleted)
|
||||||
|
|
||||||
|
# then delete the inventory itself
|
||||||
|
inventory.delete()
|
||||||
|
logger.info('Batched deletion of inventory %d complete (%d hosts removed)', inventory.id, total_deleted)
|
||||||
|
|
||||||
|
|
||||||
@task(queue=get_task_queuename, timeout=3600 * 5)
|
@task(queue=get_task_queuename, timeout=3600 * 5)
|
||||||
def delete_inventory(inventory_id, user_id, retries=5):
|
def delete_inventory(inventory_id, user_id, retries=5):
|
||||||
# Delete inventory as user
|
# Delete inventory as user
|
||||||
@@ -1034,12 +1061,14 @@ def delete_inventory(inventory_id, user_id, retries=5):
|
|||||||
user = None
|
user = None
|
||||||
with ignore_inventory_computed_fields(), ignore_inventory_group_removal(), impersonate(user):
|
with ignore_inventory_computed_fields(), ignore_inventory_group_removal(), impersonate(user):
|
||||||
try:
|
try:
|
||||||
Inventory.objects.get(id=inventory_id).delete()
|
inv = Inventory.objects.get(id=inventory_id)
|
||||||
emit_channel_notification('inventories-status_changed', {'group_name': 'inventories', 'inventory_id': inventory_id, 'status': 'deleted'})
|
|
||||||
logger.debug('Deleted inventory {} as user {}.'.format(inventory_id, user_id))
|
|
||||||
except Inventory.DoesNotExist:
|
except Inventory.DoesNotExist:
|
||||||
logger.exception("Delete Inventory failed due to missing inventory: " + str(inventory_id))
|
logger.exception("Delete Inventory failed due to missing inventory: " + str(inventory_id))
|
||||||
return
|
return
|
||||||
|
try:
|
||||||
|
_batched_delete_inventory(inv)
|
||||||
|
emit_channel_notification('inventories-status_changed', {'group_name': 'inventories', 'inventory_id': inventory_id, 'status': 'deleted'})
|
||||||
|
logger.debug('Deleted inventory {} as user {}.'.format(inventory_id, user_id))
|
||||||
except DatabaseError:
|
except DatabaseError:
|
||||||
logger.exception('Database error deleting inventory {}, but will retry.'.format(inventory_id))
|
logger.exception('Database error deleting inventory {}, but will retry.'.format(inventory_id))
|
||||||
if retries > 0:
|
if retries > 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user