Skip to content

Commit 439f0b6

Browse files
authored
Account for ignorable payloads in retry script, try to prevent them in runner (#1000)
* Move guard to check for valid action before refreshing bug data * We never log the string "skipping events", so remove assertions that those logs don't exist * Slightly change mocking in test_retry - use `mocker`fixture for pytests fixtures - change import to import mock module - use more descriptive name for function that returns a mock with an aiter error * Account for invalid requests in queue processing
1 parent 4b27086 commit 439f0b6

File tree

4 files changed

+48
-18
lines changed

4 files changed

+48
-18
lines changed

jbi/retry.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import jbi.runner as runner
1111
from jbi.configuration import ACTIONS
12+
from jbi.errors import IgnoreInvalidRequestError
1213
from jbi.queue import get_dl_queue
1314

1415
CONSTANT_RETRY = getenv("DL_QUEUE_CONSTANT_RETRY", "false") == "true"
@@ -62,6 +63,10 @@ async def retry_failed(item_executor=runner.execute_action, queue=get_dl_queue()
6263
item_executor(item.payload, ACTIONS)
6364
await queue.done(item)
6465
metrics["events_processed"] += 1
66+
except IgnoreInvalidRequestError:
67+
logger.warning("removing invalid event %s", item.identifier)
68+
await queue.done(item)
69+
metrics["events_processed"] += 1
6570
except Exception:
6671
metrics["events_failed"] += 1
6772
logger.exception(

jbi/runner.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,13 @@ def execute_action(
217217
if bug.is_private:
218218
raise IgnoreInvalidRequestError("private bugs are not supported")
219219

220+
try:
221+
action = lookup_action(bug, actions)
222+
except ActionNotFoundError as err:
223+
raise IgnoreInvalidRequestError(
224+
f"no bug whiteboard matching action tags: {err}"
225+
) from err
226+
220227
logger.info(
221228
"Handling incoming request",
222229
extra=runner_context.model_dump(),
@@ -229,12 +236,7 @@ def execute_action(
229236
raise IgnoreInvalidRequestError(str(err)) from err
230237

231238
runner_context = runner_context.update(bug=bug)
232-
try:
233-
action = lookup_action(bug, actions)
234-
except ActionNotFoundError as err:
235-
raise IgnoreInvalidRequestError(
236-
f"no bug whiteboard matching action tags: {err}"
237-
) from err
239+
238240
runner_context = runner_context.update(action=action)
239241

240242
linked_issue_key: Optional[str] = bug.extract_from_see_also(

tests/unit/test_retry.py

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
from datetime import UTC, datetime, timedelta
2-
from unittest.mock import MagicMock
2+
from unittest import mock
33

44
import pytest
55

6+
from jbi.errors import IgnoreInvalidRequestError
67
from jbi.retry import RETRY_TIMEOUT_DAYS, retry_failed
78
from jbi.runner import execute_action
89

910

10-
def iter_error():
11-
mock = MagicMock()
12-
mock.__aiter__.return_value = None
13-
mock.__aiter__.side_effect = Exception("Throwing an exception")
14-
return mock
11+
def mock_aiter_error():
12+
_mock = mock.MagicMock()
13+
_mock.__aiter__.return_value = None
14+
_mock.__aiter__.side_effect = Exception("Throwing an exception")
15+
return _mock
1516

1617

1718
async def aiter_sync(iterable):
@@ -20,8 +21,8 @@ async def aiter_sync(iterable):
2021

2122

2223
@pytest.fixture
23-
def mock_executor():
24-
return MagicMock(spec=execute_action)
24+
def mock_executor(mocker):
25+
return mocker.MagicMock(spec=execute_action)
2526

2627

2728
@pytest.mark.asyncio
@@ -116,7 +117,6 @@ async def test_retry_remove_expired(
116117
len(mock_queue.done.call_args_list) == 2
117118
), "both items should have been marked as done"
118119
assert caplog.text.count("failed to reprocess event") == 0
119-
assert caplog.text.count("skipping events") == 0
120120
assert caplog.text.count("removing expired event") == 1
121121
mock_executor.assert_called_once() # only one item should have been attempted to be processed
122122
assert metrics == {
@@ -128,18 +128,42 @@ async def test_retry_remove_expired(
128128
}
129129

130130

131+
@pytest.mark.asyncio
132+
async def test_retry_remove_invalid(
133+
caplog, mock_queue, mock_executor, queue_item_factory
134+
):
135+
mock_queue.retrieve.return_value = {
136+
1: aiter_sync(queue_item_factory.create_batch(2))
137+
}
138+
mock_executor.side_effect = [
139+
IgnoreInvalidRequestError("How did this get in here"),
140+
mock.DEFAULT,
141+
]
142+
metrics = await retry_failed(item_executor=mock_executor, queue=mock_queue)
143+
assert (
144+
len(mock_queue.done.call_args_list) == 2
145+
), "both items should have been marked as done"
146+
assert caplog.text.count("removing invalid event") == 1
147+
assert metrics == {
148+
"bug_count": 1,
149+
"events_processed": 2,
150+
"events_skipped": 0,
151+
"events_failed": 0,
152+
"bugs_failed": 0,
153+
}
154+
155+
131156
@pytest.mark.asyncio
132157
async def test_retry_bug_failed(caplog, mock_queue, mock_executor, queue_item_factory):
133158
mock_queue.retrieve.return_value = {
134159
1: aiter_sync([queue_item_factory(payload__bug__id=1)]),
135-
2: iter_error(),
160+
2: mock_aiter_error(),
136161
}
137162

138163
metrics = await retry_failed(item_executor=mock_executor, queue=mock_queue)
139164
mock_queue.retrieve.assert_called_once()
140165
mock_queue.done.assert_called_once() # one item should have been marked as done
141166
assert caplog.text.count("failed to reprocess event") == 0
142-
assert caplog.text.count("skipping events") == 0
143167
assert caplog.text.count("removing expired event") == 0
144168
assert caplog.text.count("failed to parse events for bug") == 1
145169
mock_executor.assert_called_once() # only one item should have been attempted to be processed

tests/unit/test_runner.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ def test_execution_logging_for_ignored_requests(
160160
execute_action(request=webhook, actions=actions)
161161

162162
assert capturelogs.messages == [
163-
"Handling incoming request",
164163
"Ignore incoming request: no bug whiteboard matching action tags: devtest",
165164
]
166165

0 commit comments

Comments
 (0)