Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #25939 -- Removed transaction from QuerySet.update_or_create's save(). #5804

Closed
wants to merge 1 commit into from

Conversation

bartosh
Copy link

@bartosh bartosh commented Dec 9, 2015

There is no need to wrap calls of create and save methods in
transaction.atomic as it's already done down the call stack in
Model.save_base method.

@bartosh
Copy link
Author

bartosh commented Dec 9, 2015

This is a second PR. You can see first one here: #5790

@@ -495,8 +494,7 @@ def _create_object_from_params(self, lookup, params):
Used by get_or_create and update_or_create
"""
try:
with transaction.atomic(using=self.db):
obj = self.create(**params)
obj = self.create(**params)
Copy link
Member

Choose a reason for hiding this comment

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

I think this one requires a savepoint, but create() calls save(), and save has savepoint=False.

Copy link
Member

Choose a reason for hiding this comment

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

Also, the atomic(savepoint=False) is really cheap (no DB action at all) when used inside another atomic. So, the atomic() here makes the atomic() inside save cheap enough to not matter (of course, benchmarks tell the truth).

@bartosh
Copy link
Author

bartosh commented Dec 9, 2015

Thanks, I'll update PR according to your comments.

Just wanted to say that I'm getting "OperationalError: database is locked" exception without first hunk applied. It happens on get_or_create call.
Can you suggest what can be the reason for that? I'm using sqlite if it matters.

@aaugustin
Copy link
Member

If you're getting database is locked on SQLite, most likely you shouldn't be using SQLite in the first place. I don't expect the kind of changes you're proposing here to have a significant effect.

Please seek help:

  • on the django-users mailing list to discuss what optimizations you could use in your project
  • on the django-developers mailing list to discuss changes in Django; these changes will have to be backed up by benchmarks showing an improvement

@bartosh
Copy link
Author

bartosh commented Dec 9, 2015

Thanks for your suggestion. I can only say that with this patch applied I can't reproduce the error. There should be a reason for that. Suggesting to stop using sqlite is a work around, not a solution from my point of view.

@bartosh
Copy link
Author

bartosh commented Dec 9, 2015

Updated PR according to reviewer suggestions. Please, merge if you don't mind.

@aaugustin
Copy link
Member

@bartosh
Copy link
Author

bartosh commented Dec 9, 2015

aaugustin, thank you for the info. I hope it will help me to find the reason for the issue.
Just checked out what you've pointed to:
ATOMIC_REQUESTS is not in use - true
update_or_create ends up creating an object - true
the save method of that objects does long things like sending email after the write (i.e. once it holds an exclusive lock on the database) - not true. save is not redefined in the model.

Just to be clear. The issue is gone when both hunks of original patch are applied. It's still there with moving save out of transaction.atomic context. I'll check if another hunk alone fixes it or not.

@bartosh
Copy link
Author

bartosh commented Dec 9, 2015

aaugustin, I've read sqlite FAQ before. what's written there doesn't explain the fact that I'm getting dozens of 'database is locked' issues and they're gone if I move obj = self.create(**params) out of transaction.atomic context in query.py

Any other ideas where to look and what potentially could be the reason?

@aaugustin
Copy link
Member

I have no idea about your use case, your code, or the kind of concurrency you're facing. If you could reproduce this with a minimal example and a simulate load, then we could benchmark it.

@bartosh
Copy link
Author

bartosh commented Dec 10, 2015

yep, makes sense. I'll try to sketch some example. meanwhile feel free to merge this commit. I'll create new PR when I get the data.

Thank you,
Ed

@timgraham
Copy link
Member

Could you add a mention in the 1.10 release notes "Backwards-incompatible" changes section as requested by Aymeric? "This may affect query counts tested by assertNumQueries in TransactionTestCase which should be mentioned in the release notes."

There is no need to wrap call of save method in transaction.atomic
as it's already done down the call stack in Model.save_base method.
@bartosh bartosh closed this Dec 14, 2015
@bartosh
Copy link
Author

bartosh commented Dec 14, 2015

reopen as it was closed by mistake.

@bartosh bartosh changed the title don't wrap create and save in transaction.atomic don't wrap save in transaction.atomic Dec 14, 2015
@bartosh
Copy link
Author

bartosh commented Dec 14, 2015

Updated release notes as suggested.

@bartosh bartosh reopened this Dec 14, 2015
@timgraham timgraham changed the title don't wrap save in transaction.atomic Fixed #25939 -- Removed transaction from QuerySet.update_or_create's save(). Dec 15, 2015
@timgraham
Copy link
Member

merged in 423b3af, thanks!

@timgraham timgraham closed this Dec 15, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants