Surely B’s insert would be rejected only if there’s a uniqueness constraint on the fields being updated? Otherwise you’ll get two rows with identical data but different ids.
If there’s a constraint then you can catch the error after the insert_or_update, find the existing id, and then re-run the function, which will update.
No, this is a classic example of a race condition, which you identified in your example (A and B trying to insert a record with the same id)
Multi is kind of irrelevant with respect to race conditions, I assume what you’re asking is “would wrapping this code in a transaction help?”. The answer is: no, it won’t help in the general case. It would only help if the transaction isolation level of your DB is set to “serializable”, which is typically not the case for performance reasons.
insert_or_update is not there to avoid race conditions, but as a convenience function that allows you to persist a changeset regardless of whether the underlying data is persisted in the DB or not (otherwise you’d have to choose insert or update)
You should check the result of MyRepo.insert_or_update(): if the result is {:error, changeset} and the changeset contains a primary key constraint violation, then you know that you hit the race condition and you can react appropriately.