Reputation: 1441
Can anyone advise on the following problem:
I have a custom get_or_create
method, which checks multiple fields and does some fancy stuff upon creation:
def fancy_get_or_create(name):
object = self.fancy_get(name)
if not object:
object = self.fancy_create(name)
return object
def fancy_get(name):
return self.filter(Q(name=name) | Q(alias=name)).first()
def fancy_create(name):
name = self.some_preprocessing(name)
return self.create(name=name, alias=name)
There's a race condition, where one request will check to see if the object exists, find nothing, and start creating it. Before that request finishes creating the object, another request comes in looking for the same object, finds, nothing, and begins creating the new object. This request will fail because the database has some uniqueness constraints (the previous request had just created the object, so the second request will fail).
Is there any way to prevent request 2 from querying the database until request 1 has finished? I was reading about transaction management and it did not seem like the solution, since the issue is not partial updates (which would suggest an atomic transaction), but rather the need to make the second request wait until the first has finished.
Thanks!
Update: Here's what I went with:
try:
return self.fancy_get(name) or self.fancy_create(name)
except IntegrityError:
return self.fancy_get(name)
Upvotes: 2
Views: 1390
Reputation: 810
There are two viable solutions:
Use a mutex so only one process can access the fancy_get_or_create function at the same time.
Capture the error thrown by the database and do something instead: ignore that create, update the row instead of creating it, throw an exception, etc.
Edit: another solution might be doing an INSERT IGNORE instead of just an INSERT. https://dev.mysql.com/doc/refman/5.1/en/insert.html
Upvotes: 3