Switch session create to check oauth_github first, then get user#13856
Conversation
oauth_github first, then get useroauth_github first, then get user
This comment has been minimized.
This comment has been minimized.
97aa9a7 to
9eb725c
Compare
This comment has been minimized.
This comment has been minimized.
|
@Turbo87 Ok, I rebased to pick up your new test and then thought a lot about the transactions to fix the problem. I think the transactions don't need to be nested, but as I've noted in the commit message of the last commit and in comments, we're going to have to rethink how the transactions and errors and inserts and updates work when the users table bases its uniqueness on Let me know if you/Claude think this is correct now! |
This comment has been minimized.
This comment has been minimized.
9eb725c to
053f674
Compare
This comment has been minimized.
This comment has been minimized.
|
@Turbo87 this is ready for re-review! |
We have `new_user`, which is a crates.io `NewUser`. I'm about to add usage of crates.io `User`s, which I think will be confusing unless this varibale name changes.
These actions don't all need to be in one transaction. We need: - The update to `oauth_github` and `users` for an existing user to happen in a transaction - The creation of `oauth_github` and `users` for a new user to happen in a transaction If updating a user fails because the connection is read-only, fall back to looking up the existing user without an update. In that case, we shouldn't even try to create a new user if the user doesn't exist, because that won't work either. Then, rather than nesting the transaction in `create_or_update_user`, move the creation of the `oauth_github` record into that transaction. == Race condition analysis == If the earlier "update" transaction fails because the record to update doesn't exist, then in some other request the records get created before this request's "insert" transaction runs, for now the "ON CONFLICT (gh_id) DO UPDATE" in `NewUser.insert_or_update` and the "ON CONFLICT (account_id) DO UPDATE" in the `oauth_github` insert that are in this request's "insert" transaction should work. When we switch the `users` table to be unique on `users.username` rather than `users.gh_id`, we need to change how this code works. If the username is already taken by a different crates.io account with a different associated `oauth_github`, we need to prompt the current user to choose a different username. If the current user is the same user whose account got created in between the statements of this request, we should log in this user.
And verify with a test.
This is Rust, not Ruby, also this comment won't get rendered anywhere so it doesn't need to be linked.
053f674 to
cc4431e
Compare
This comment has been minimized.
This comment has been minimized.
|
@Turbo87 Ready for re-re-review! I rebased and added new commits, but didn't change any of the commits you've already looked at.
omg yes 😭 filing a new issue for that now-- it's sort of related to some of the username issues but can be done first as a straight refactoring i think |
|
@Turbo87 ready for re-review! |
There was a problem hiding this comment.
I have a couple of comment nits, but otherwise this makes sense to me behaviourally. Approved, although I suspect @Turbo87 will want to take another look too.
I do kinda wonder if we should split create_or_update_user to have each path in a separate function for readability reasons, but I don't feel very strongly about it.
Co-authored-by: Adam Harvey <adam@adamharvey.name>
Yeah, I think that will be done soon as part of #13907. |
Use a transaction inside update_user so it's impossible to call incorrectly, rather than relying on the caller to use a transaction.
But only restore an insert method, not insert_or_update. Also make last_sync have a default value
Let it be known that I think race conditions necessitating an update here will be possible and that the upsert would be friendlier, but that I suppose the user getting an error and reloading and having it work isn't the worst thing ever
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
7d622d7 to
e3f4fe2
Compare
When session::save_user_to_database was doing upserts for oauth_github records outside of a transaction with updates of user records, the test `user::github_with_email_does_not_overwrite_email` passed by coincidence. But now that we're switching the session update to try updating an oauth_github record first and assuming that if an oauth_github record doesn't exist then the user doesn't exist either, and then doing only an insert rather than an upsert if we're creating new records, the test started failing because TestApp with_user, which calls db_new_user, wasn't actually creating an oauth_github record for the user. Ensure all tests using db_new_user are realistic and valid by creating an oauth_github record for the user, much like we create a verified email address. This updated a lot of snapshots.
e3f4fe2 to
a5bbce6
Compare
|
@Turbo87 @LawnGnome Ready for re-review! ❤️ |
Explicitly set an avatar in the og image tests that need it.
|
@Turbo87 @LawnGnome Ready for re-review! ❤️ |
This is what I was talking about in this comment, that we can't use nice upserts but need to do multiple update/select/inserts in a transaction instead, so that we switch to using
oauth_githubas the source of truth of whether we're logging in an existing user or a new user.I've extracted this from #13832 and I think making this change first is needed before we can stop using
users.{gh_id, gh_avatar, gh_encrypted_token}.