Skip to content

Commit 88ca8fa

Browse files
committed
fix(receiver): ack messages that cannot be parsed or resolved
When a message fails formatter parsing or names a task the broker cannot find, the receiver logged a warning and returned without acknowledging. For ackable brokers (e.g. RabbitMQ) the message stayed unacked and pending forever: it was already prefetched out of the queue, so purging the queue did not clear it and it only reappeared when the consumer was terminated. Both early-return paths now ack the message when it is ackable, since a message that cannot be parsed or whose task does not exist will never become processable by a redelivery. Adds regression tests for both paths. Closes #569
1 parent ae2b788 commit 88ca8fa

2 files changed

Lines changed: 54 additions & 0 deletions

File tree

taskiq/receiver/receiver.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ async def callback( # noqa: C901, PLR0912
130130
exc,
131131
exc_info=True,
132132
)
133+
if ack_controller.is_ackable:
134+
await ack_controller.ack()
133135
return
134136
logger.debug(f"Received message: {taskiq_msg}")
135137
task = self.broker.find_task(taskiq_msg.task_name)
@@ -138,6 +140,8 @@ async def callback( # noqa: C901, PLR0912
138140
'task "%s" is not found. Maybe you forgot to import it?',
139141
taskiq_msg.task_name,
140142
)
143+
if ack_controller.is_ackable:
144+
await ack_controller.ack()
141145
return
142146
logger.debug(
143147
"Function for task %s is resolved. Executing...",

tests/receiver/test_receiver.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,56 @@ def ack_callback() -> None:
334334
assert acked
335335

336336

337+
async def test_callback_acks_unknown_task() -> None:
338+
"""Test that a message for an unknown task is acked, not left pending."""
339+
broker = InMemoryBroker()
340+
acked = False
341+
342+
def ack_callback() -> None:
343+
nonlocal acked
344+
acked = True
345+
346+
receiver = get_receiver(broker)
347+
348+
broker_message = broker.formatter.dumps(
349+
TaskiqMessage(
350+
task_id="task_id",
351+
task_name="unknown_task_name",
352+
labels={},
353+
args=[],
354+
kwargs={},
355+
),
356+
)
357+
358+
await receiver.callback(
359+
AckableMessage(
360+
data=broker_message.message,
361+
ack=ack_callback,
362+
),
363+
)
364+
assert acked
365+
366+
367+
async def test_callback_acks_unparsable_message() -> None:
368+
"""Test that an unparsable message is acked, not left pending."""
369+
broker = InMemoryBroker()
370+
acked = False
371+
372+
def ack_callback() -> None:
373+
nonlocal acked
374+
acked = True
375+
376+
receiver = get_receiver(broker)
377+
378+
await receiver.callback(
379+
AckableMessage(
380+
data=b"not a valid taskiq message",
381+
ack=ack_callback,
382+
),
383+
)
384+
assert acked
385+
386+
337387
async def test_callback_success_ackable_async() -> None:
338388
"""Test that acks work with async functions."""
339389
broker = InMemoryBroker()

0 commit comments

Comments
 (0)