From 229e997e7e2a4bde6ac91d7fc4f229b78c40934c Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 6 Feb 2019 17:07:12 -0500 Subject: [PATCH] don't update parent event changed|failed in bulk (it's expensive) --- awx/main/models/events.py | 20 ++++----- .../tests/functional/models/test_events.py | 44 +++++++++++++++++++ 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/awx/main/models/events.py b/awx/main/models/events.py index 1a0cdb4ce6..b92866e889 100644 --- a/awx/main/models/events.py +++ b/awx/main/models/events.py @@ -352,9 +352,16 @@ class BasePlaybookEvent(CreatedModifiedModel): if hasattr(self, 'job') and not from_parent_update: if getattr(settings, 'CAPTURE_JOB_EVENT_HOSTS', False): self._update_hosts() - if self.event == 'playbook_on_stats': - self._update_parents_failed_and_changed() + if self.parent_uuid: + kwargs = {} + if self.changed is True: + kwargs['changed'] = True + if self.failed is True: + kwargs['failed'] = True + if kwargs: + JobEvent.objects.filter(job_id=self.job_id, uuid=self.parent_uuid).update(**kwargs) + if self.event == 'playbook_on_stats': hostnames = self._hostnames() self._update_host_summary_from_stats(hostnames) try: @@ -435,15 +442,6 @@ class JobEvent(BasePlaybookEvent): updated_fields.add('host_name') return updated_fields - def _update_parents_failed_and_changed(self): - # Update parent events to reflect failed, changed - runner_events = JobEvent.objects.filter(job=self.job, - event__startswith='runner_on') - changed_events = runner_events.filter(changed=True) - failed_events = runner_events.filter(failed=True) - JobEvent.objects.filter(uuid__in=changed_events.values_list('parent_uuid', flat=True)).update(changed=True) - JobEvent.objects.filter(uuid__in=failed_events.values_list('parent_uuid', flat=True)).update(failed=True) - def _update_hosts(self, extra_host_pks=None): # Update job event hosts m2m from host_name, propagate to parent events. extra_host_pks = set(extra_host_pks or []) diff --git a/awx/main/tests/functional/models/test_events.py b/awx/main/tests/functional/models/test_events.py index 29c17e1ba7..d16c60bb60 100644 --- a/awx/main/tests/functional/models/test_events.py +++ b/awx/main/tests/functional/models/test_events.py @@ -7,6 +7,50 @@ from awx.main.models import (Job, JobEvent, ProjectUpdate, ProjectUpdateEvent, SystemJobEvent) +@pytest.mark.django_db +@mock.patch('awx.main.consumers.emit_channel_notification') +def test_parent_changed(emit): + j = Job() + j.save() + JobEvent.create_from_data(job_id=j.pk, uuid='abc123', event='playbook_on_task_start') + assert JobEvent.objects.count() == 1 + for e in JobEvent.objects.all(): + assert e.changed is False + + JobEvent.create_from_data( + job_id=j.pk, + parent_uuid='abc123', + event='runner_on_ok', + event_data={ + 'res': {'changed': ['localhost']} + } + ) + assert JobEvent.objects.count() == 2 + for e in JobEvent.objects.all(): + assert e.changed is True + + +@pytest.mark.django_db +@pytest.mark.parametrize('event', JobEvent.FAILED_EVENTS) +@mock.patch('awx.main.consumers.emit_channel_notification') +def test_parent_failed(emit, event): + j = Job() + j.save() + JobEvent.create_from_data(job_id=j.pk, uuid='abc123', event='playbook_on_task_start') + assert JobEvent.objects.count() == 1 + for e in JobEvent.objects.all(): + assert e.failed is False + + JobEvent.create_from_data( + job_id=j.pk, + parent_uuid='abc123', + event=event + ) + assert JobEvent.objects.count() == 2 + for e in JobEvent.objects.all(): + assert e.failed is True + + @pytest.mark.django_db @mock.patch('awx.main.consumers.emit_channel_notification') def test_job_event_websocket_notifications(emit):