🛠️ Improvement: crash-safe publication of project invitation tasks
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 59.6k
- Forks
- 5.8k
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 49
Description
Split out of #9812, which restores the project invitation endpoint from a guaranteed HTTP 500. This is the reliability gap underneath it, left out of that PR deliberately to keep the fix reviewable.
ProjectInvitationsViewset.create persists the invitations and then publishes a task per row:
created_invitations = ProjectMemberInvite.objects.bulk_create(
project_invitations, batch_size=10, ignore_conflicts=True
)
for invitation in created_invitations:
project_invitation.delay(...)
The rows are committed before the first delay(). If the broker is unreachable partway through, earlier invitations exist and have been emailed, later ones exist and have not, and the endpoint returns 500. A client that retries then re-sends invitations that already went out, since bulk_create(ignore_conflicts=True) will not recreate the row but the loop will publish for it again.
Worth covering:
- persist an outbox record in the same transaction as the invitation, so the intent to send is committed atomically with the row
- publish from the outbox with retries, rather than inline in the request
- key idempotency on
ProjectMemberInviteid so a retry cannot email the same invitee twice
WorkspaceInvitationsViewset has the same shape, so whatever lands here probably wants to apply there too.
Happy to pick this up if it is wanted, though the outbox table and where the publisher runs are design calls worth agreeing first.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at ProjectInvitationsViewset.create and compare its bulk_create and project_invitation.delay flow with WorkspaceInvitationsViewset. Agree the outbox table, publisher location, retry behavior, and ProjectMemberInvite idempotency approach before implementation. Done means invitation persistence and send intent are atomic, publishing retries safely, and request retries do not resend earlier invitations.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- django, python
- Domain
- backend-api-design, databases, distributed-systems
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100