Skip to content

Fix post_send firing after task execution with await_inplace=True#639

Open
apoorvdarshan wants to merge 1 commit into
taskiq-python:masterfrom
apoorvdarshan:fix/inplace-post-send-order
Open

Fix post_send firing after task execution with await_inplace=True#639
apoorvdarshan wants to merge 1 commit into
taskiq-python:masterfrom
apoorvdarshan:fix/inplace-post-send-order

Conversation

@apoorvdarshan

Copy link
Copy Markdown

Summary

Fixes #586.

With InMemoryBroker(await_inplace=True), the client-side post_send middleware hook fired after the task finished executing, giving the ordering:

pre_send -> pre_execute -> task -> post_execute -> post_send

instead of the expected:

pre_send -> post_send -> pre_execute -> task -> post_execute

This breaks the semantics of post_send (e.g. a "task queued" log ends up with a later timestamp than "task started").

Root cause

InMemoryBroker.kick() awaited the full receiver callback (running pre_execute -> task -> post_execute) inline before control returned to AsyncKicker.kiq(), which only then invoked the post_send middleware hook.

Fix

  • InMemoryBroker.kick() now schedules the in-place receiver as an asyncio.Task and returns immediately (into a dedicated _inplace_tasks set) instead of awaiting it inline. This lets post_send run before the task starts executing.
  • A new AsyncBroker._finish_kick() hook (a no-op by default) is awaited in AsyncKicker.kiq() right after the post_send loop. InMemoryBroker overrides it to await the scheduled in-place task.

The task is still fully executed before kiq() returns, so the existing await_inplace contract is preserved (result ready on return, no leftover running tasks). Other brokers are unaffected: _finish_kick() defaults to a no-op.

Tests

  • Added tests/brokers/test_inmemory.py::test_inplace_middleware_order, which asserts the hook order is pre_send -> post_send -> pre_execute -> task -> post_execute and that the task is fully executed by the time kiq() returns. This test fails on master and passes with the fix.
  • Full suite green locally (pytest -n auto, 293 passed), plus black, ruff, and mypy (strict) all clean.

Disclosure: prepared with AI assistance; reviewed and verified locally.

With InMemoryBroker(await_inplace=True) the middleware hooks fired in the
order pre_send -> pre_execute -> task -> post_execute -> post_send, because
kick() awaited the full receiver callback (pre_execute/task/post_execute)
before control returned to the sender that invokes post_send.

Now kick() schedules the in-place receiver as a task and returns
immediately, and the task is awaited in a new _finish_kick() hook that runs
right after post_send. This restores the expected ordering
pre_send -> post_send -> pre_execute -> task -> post_execute while the task
is still fully executed before kiq() returns.

Fixes taskiq-python#586.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

post_send fires after task execution with await_inplace=True

1 participant