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
Duplicate Key Error (maybe not related with Fastapi-user but help needed) #349
Comments
Also a precision concerning the uuid use, here is my User class, I set uuid here because I need to have it in str format in the field uuid_str (and I didn't find a way to do it without updating the object in an other query after registration by getting the value set with the default validator in models.BaseUser) class User(models.BaseUser):
id: Optional[UUID4] = uuid.uuid4()
uuid_str: Optional[str] = str(id)
[...] |
Hi @MariusMez! I think there is something wrong with your model here. The way I see it, you generate the UUID when the source code is loaded, and never after. A bit like when we mistakenly use mutable default arguments (see https://docs.python-guide.org/writing/gotchas/). Simple reproducible example: import uuid
from pydantic import BaseModel, UUID4
class User(BaseModel):
id: UUID4 = uuid.uuid4()
u1 = User()
u2 = User()
print(u1.id, u2.id) # UUID are the same I think it works most of the time because Gunicorn should spawn a new process, and thus the code is reloaded with a new value. However, when there is concurrency, the same process is reused and they end up using the same id. To circumvent this, you should feed the value at runtime. Validators in Pydantic are here for this purpose. So your model could look like: from typing import Optional
from fastapi_users import models
from pydantic import root_validator
class User(models.BaseUser):
uuid_str: Optional[str] = None
# root_validator allows to get the values of other fields once they are all validated
@root_validator
def stringify_uuid(cls, values):
id = values.get("id")
values["uuid_str"] = str(id)
return values |
Thanks @frankie567 for your answer and your explanation about the Pydantic validators I didn't use correctly. Much appreciated 👍 I have to dig more on this, because now I have the same error but for the email field (wich I didn't redefine in my User model )
|
You're welcome! Hmm, that's weird indeed because existing e-mail should be checked in |
Yes very strange as I can't reproduce the problem on my computer (with a local mongo) using the same launch command with gunicorn workers. Everything is working great In fact I "solved the problem" by deleting one index on my mongo prod instance (a cluster in Mongodb cloud atlas). The one related to unique email (I didn't delete the one on case_insensitive email). Deleting this unique email field index solve the error and the registring process goes well (no duplicate, email existing checking case_insensitive is ok) So for the moment I can close this ticket, but I'm wondering what is the real issue behind this... And to answer your questions, I didn't override the register route nor the email_collation parameter. |
Hmm... There is something wrong with those indices ; I'll try to dig more into this. |
And of course when I restart the application, the missing indexes are newly created and the problem re-appear :D |
Yeah, sure they are defined here: The way it should work:
So, theoretically, this should work. Can you reproduce it locally? If so, could you share the steps you are following? |
Locally, it's working perfectly. But with a "prod Mongo", the problem appears every time but only after two registrations. And if I delete the last user in the database, it allow two more registrations before the DuplicateKey error. (I tried with more than 1 gunicorn workers, and also directly with unicorn and 1 worker, the same) When I delete the unique index on email it works again. I test to delete this unique email index also locally and after that the email checking (case-insensitive) is working well. So maybe this unique email index is redondant with the case_insensitive_email_index wich seems to be good alone. |
So, if I understand correctly:
Is that it? |
Not really, initially the issue raise in this case:
--> Before removing the unique email index, to allow the third guy to register I have to restart the application and it will go for two more registrations before the error, or deleting an entry in the user collection. For the workflow I didn't test with a 1bis. guy trying to register twice with "email@gmail.com" but the email checking was working. |
🤯 |
And of course for my local mongodb, no problem. The fix I found and which seems good for now is to remove the unique email index (only this one). |
Random thought: you said you were on an Atlas cluster ; are you using the right connection URL that handle clusters correctly ? In that form |
Yes but without specifying the dbname |
I suspect there are some gotchas with the cluster/sharded nature of Atlas servers. There are a number of restrictions about this in the MongoDB doc I can't fully understand : https://docs.mongodb.com/manual/core/index-unique/ What you can do to circumvent this while we figure this out is to override the class MongoDBUserDatabaseWithoutIndex(MongoDBUserDatabase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.collection.drop_index("email_1") |
Thanks for the temporary fix, like you I don't understand well the differences |
FYI I've updated my mongo Atlas Cluster version from 4.2 to 4.4 and it seems the problem is gone... All is working OK with all the indexes. @frankie567 Sorry for the headache and thanks for the Pydantic tips ! I let you close this issue ;-) |
Glad to hear it! I'm also on an Atlas Cluster in 4.2 ; I didn't have this issue yet, fingers crossed 🤞 |
Hi @frankie567
Sometimes in a "production" application using fastapi-user (with Gunicorn and uvicorn and concurrency), I have this error I'm not unable to reproduce by myself in dev mode.
It occurs sometime at registration when two users want to register in the same two minutes.
I don't know if it's related to the use of Fastapi-user but maybe I can find some help here to understand this error :-)
In fact this UUID mentioned in the error below appear to be the uuid of the previous saved user. I'm wondering if it's not a concurrency issue with gunicorn?
The text was updated successfully, but these errors were encountered: