Opened 61 minutes ago

Last modified 57 minutes ago

#37479 assigned Cleanup/optimization

Run async signal receivers sequentially

Reported by: Mykhailo Havelia Owned by: Mykhailo Havelia
Component: Core (Other) Version: 6.1
Severity: Low Keywords: async signals transaction
Cc: Mykhailo Havelia Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Since async signal support was added, Signal.send() and Signal.asend() run async receivers concurrently. This causes two problems:

  • Registration order is lost. In sync Django, receivers run in the order they were registered, and projects often rely on this for good reasons. With async receivers, the order is lost silently, so converting a receiver from sync to async can change behavior.
  • Transactions inside receivers are not well ordered. Since each receiver runs in its own task, their queries and savepoints can interleave. This came up in the review of async transaction.atomic() (#33882, ​PR #22074): receivers can't use atomic() inside an async transaction, and inside a sync transaction a write from one receiver can be silently rolled back with another receiver's savepoint.

Proposal: await async receivers one by one, in registration order, in the caller's task. This keeps the same semantics as sync receivers and makes transaction handling inside receivers safe.

Trade-off: slow receivers would no longer run concurrently. This is a behavior change and would need a release note.

Change History (2)

comment:1 by Mykhailo Havelia, 60 minutes ago

Owner: set to Mykhailo Havelia
Status: new → assigned

comment:2 by Simon Charette, 57 minutes ago

Keywords: signals transaction added
Triage Stage: Unreviewed → Accepted
Note: See TracTickets for help on using tickets.
Back to Top