-
Notifications
You must be signed in to change notification settings - Fork 22
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
chore: requery account for transaction isolation #700
Conversation
/retest |
@@ -76,6 +77,15 @@ func (x *accountDao) GetOrCreateByIdentity(ctx context.Context, orgId string, ac | |||
return nil, fmt.Errorf("pgx error: %w", err) | |||
} | |||
|
|||
// Step 4: requery in case transaction isolation (simultaneous transactions) | |||
if result.ID == 0 && result.OrgID == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Query again makes sense (it will increase the chances of success :) ), but will we ever trigger this, given we return on error few lines before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right, I thought empty result is not an error while in fact it actually is. Rebased a new version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm good, but the linter ain't happy now.
internal/dao/pgx/account_pgx.go
Outdated
err := pgxscan.Get(ctx, db.Pool, result, query, orgId, account) | ||
if err != nil { | ||
return nil, fmt.Errorf("pgx requery error: %w", err) | ||
} | ||
} | ||
} else if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Linter doesn't like shadowing the var.
We can use the same var and use the same if for catching all the "leftover" errors in the main, right?
Smthing like:
err := pgxscan.Get(ctx, db.Pool, result, query, orgId, account) | |
if err != nil { | |
return nil, fmt.Errorf("pgx requery error: %w", err) | |
} | |
} | |
} else if err != nil { | |
err = pgxscan.Get(ctx, db.Pool, result, query, orgId, account) | |
} | |
} | |
if err != nil { |
or just use requeryErr
var would also work :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebased
Signed-off-by: Lukáš Zapletal <[email protected]>
/retest |
All good. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @lzap ! 👍
So I came up with this nice trick on how to quickly create accounts without explicit transactions. Works great, except it does not :-) When there are actually two requests with the same account at the same time, the query is executed in an implicit transactions which cannot be turned off and I totally forgot about it.
What happens is that one request will fail with
pgx error: scanning one: no rows in result set
(https://glitchtip.devshift.net/insights/issues/973561) because although the conflict will trigger, the default isolation level in postgres will not allow to see the inserted record.The solution is to execute the same query one more time, this only happens rarely, I saw 20 events in production in one week. But it is worth fixing this.
This is hard to test or reproduce, so review carefully. Maybe with some arbitrary sleep via
pg_sleep()
function but I find this difficult to figure out. I think the code is good (TM).