From acb6d9c4d1b5973accabb06f27018778e105dee6 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Fri, 21 Jun 2019 08:46:17 -0400 Subject: [PATCH] wrap smart inv cache update w/ advisory lock * Two job templates that use the same smart inventory running at the same time can easily race to recompute the smart inventory <-> host mapping. In this case, bulk_create() can throw an error when racing. * The per-smart-inventory advisory lock ensures that the state of the system is consistent & that bulk_create() runs in isolation. --- awx/main/tasks.py | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index d73737a4b3..08b7cf779d 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -602,25 +602,26 @@ def update_inventory_computed_fields(inventory_id, should_update_hosts=True): def update_smart_memberships_for_inventory(smart_inventory): - current = set(SmartInventoryMembership.objects.filter(inventory=smart_inventory).values_list('host_id', flat=True)) - new = set(smart_inventory.hosts.values_list('id', flat=True)) - additions = new - current - removals = current - new - if additions or removals: - with transaction.atomic(): - if removals: - SmartInventoryMembership.objects.filter(inventory=smart_inventory, host_id__in=removals).delete() - if additions: - add_for_inventory = [ - SmartInventoryMembership(inventory_id=smart_inventory.id, host_id=host_id) - for host_id in additions - ] - SmartInventoryMembership.objects.bulk_create(add_for_inventory) - logger.debug('Smart host membership cached for {}, {} additions, {} removals, {} total count.'.format( - smart_inventory.pk, len(additions), len(removals), len(new) - )) - return True # changed - return False + with advisory_lock('update_smart_memberships_for_inventory-{}'.format(smart_inventory.id)): + current = set(SmartInventoryMembership.objects.filter(inventory=smart_inventory).values_list('host_id', flat=True)) + new = set(smart_inventory.hosts.values_list('id', flat=True)) + additions = new - current + removals = current - new + if additions or removals: + with transaction.atomic(): + if removals: + SmartInventoryMembership.objects.filter(inventory=smart_inventory, host_id__in=removals).delete() + if additions: + add_for_inventory = [ + SmartInventoryMembership(inventory_id=smart_inventory.id, host_id=host_id) + for host_id in additions + ] + SmartInventoryMembership.objects.bulk_create(add_for_inventory) + logger.debug('Smart host membership cached for {}, {} additions, {} removals, {} total count.'.format( + smart_inventory.pk, len(additions), len(removals), len(new) + )) + return True # changed + return False @task()