optimize the callback receiver to buffer writes on high throughput

additionaly, optimize away several per-event host lookups and
changed/failed propagation lookups

we've always performed these (fairly expensive) queries *on every event
save* - if you're processing tens of thousands of events in short
bursts, this is way too slow

this commit also introduces a new command for profiling the insertion
rate of events, `awx-manage callback_stats`

see: https://github.com/ansible/awx/issues/5514
This commit is contained in:
Ryan Petrello
2020-01-08 16:14:47 -05:00
parent 862fafab86
commit 306f504fb7
17 changed files with 370 additions and 495 deletions

View File

@@ -60,7 +60,7 @@ class TestReplayJobEvents():
r.emit_job_status = lambda job, status: True
return r
@mock.patch('awx.main.management.commands.replay_job_events.emit_channel_notification', lambda *a, **kw: None)
@mock.patch('awx.main.management.commands.replay_job_events.emit_event_detail', lambda *a, **kw: None)
def test_sleep(self, mocker, replayer):
replayer.run(3, 1)
@@ -74,7 +74,7 @@ class TestReplayJobEvents():
mock.call(0.000001),
])
@mock.patch('awx.main.management.commands.replay_job_events.emit_channel_notification', lambda *a, **kw: None)
@mock.patch('awx.main.management.commands.replay_job_events.emit_event_detail', lambda *a, **kw: None)
def test_speed(self, mocker, replayer):
replayer.run(3, 2)

View File

@@ -1,6 +1,5 @@
from datetime import datetime
from django.utils.timezone import utc
from unittest import mock
import pytest
from awx.main.models import (JobEvent, ProjectUpdateEvent, AdHocCommandEvent,
@@ -18,16 +17,11 @@ from awx.main.models import (JobEvent, ProjectUpdateEvent, AdHocCommandEvent,
datetime(2018, 1, 1).isoformat(), datetime(2018, 1, 1)
])
def test_event_parse_created(job_identifier, cls, created):
with mock.patch.object(cls, 'objects') as manager:
cls.create_from_data(**{
job_identifier: 123,
'created': created
})
expected_created = datetime(2018, 1, 1).replace(tzinfo=utc)
manager.create.assert_called_with(**{
job_identifier: 123,
'created': expected_created
})
event = cls.create_from_data(**{
job_identifier: 123,
'created': created
})
assert event.created == datetime(2018, 1, 1).replace(tzinfo=utc)
@pytest.mark.parametrize('job_identifier, cls', [
@@ -38,24 +32,20 @@ def test_event_parse_created(job_identifier, cls, created):
['system_job_id', SystemJobEvent],
])
def test_playbook_event_strip_invalid_keys(job_identifier, cls):
with mock.patch.object(cls, 'objects') as manager:
cls.create_from_data(**{
job_identifier: 123,
'extra_key': 'extra_value'
})
manager.create.assert_called_with(**{job_identifier: 123})
event = cls.create_from_data(**{
job_identifier: 123,
'extra_key': 'extra_value'
})
assert getattr(event, job_identifier) == 123
assert not hasattr(event, 'extra_key')
@pytest.mark.parametrize('field', [
'play', 'role', 'task', 'playbook'
])
def test_really_long_event_fields(field):
with mock.patch.object(JobEvent, 'objects') as manager:
JobEvent.create_from_data(**{
'job_id': 123,
'event_data': {field: 'X' * 4096}
})
manager.create.assert_called_with(**{
'job_id': 123,
'event_data': {field: 'X' * 1023 + ''}
})
event = JobEvent.create_from_data(**{
'job_id': 123,
'event_data': {field: 'X' * 4096}
})
assert event.event_data[field] == 'X' * 1023 + ''