Skip to content

fix: rollback mail thread changes on patch creation failure - #122

Merged
JelteF merged 5 commits into
postgres:mainfrom
jonasrochasilva-prog:fix/newpatch-thread-rollback
Oct 18, 2026
Merged

JelteF merged 5 commits into
postgres:mainfrom
jonasrochasilva-prog:fix/newpatch-thread-rollback

Conversation

@jonasrochasilva-prog

@jonasrochasilva-prog jonasrochasilva-prog commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

When attaching the thread failed during patch creation, the patch was deleted again, but the MailThread created by doAttachThread was left behind. And if the failure was a database error, re-rendering the form failed because the transaction was already aborted. Now patch creation and attaching the thread happen in a single transaction that is rolled back as a whole when attaching the thread fails.

doAttachThread(
cf, patch, form.cleaned_data["threadmsgid"], request.user
)
with transaction.atomic():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole function already has transaction.atomic. So I'm unsure why this is needed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm, I understand it now.

Instead of only wrapping `doAttachThread` in a savepoint and then cleaning up
the patch by hand with `patch.delete()`, all writes of `newpatch` now happen in
a single `transaction.atomic()` block, with the exception handlers outside of
it. A failure therefore rolls back the patch, its commitfest entry, its history
and the mail thread together, so nothing can be left behind if more writes are
added to `doAttachThread` later.

Because the rollback happens before the form is re-rendered, this also works
when the failure is a database error that aborted the transaction. Previously
the follow-up queries would then fail with a `TransactionManagementError` and
the user would get a 500. The test now covers that case as well.
The database-error case already covers everything the plain Python
exception case checked, since a real failing query is the stricter
scenario that needs the exception handlers outside of the
`transaction.atomic()` block. The success test duplicated almost all of
the setup, so it is folded into the same parametrized test.
Moving patch creation into the same atomic block as `doAttachThread` also
moved `form.save()` and the `PatchOnCommitFest`/`PatchHistory` saves
under the broad `except Exception`. A failure in any of those was then
shown as "An error occurred looking up the thread in the archives" and
never logged, where it used to be a 500 with a traceback.

Now only exceptions from `doAttachThread` are caught. They are re-raised
as a private `_ThreadAttachFailed`, so they still propagate out of the
atomic block and roll everything back, and the form error is set once the
transaction is gone. Anything else fails the request like before.
@JelteF
JelteF merged commit 7c3a2f4 into postgres:main Oct 18, 2026
2 checks passed
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.

3 participants