This is a classic race condition, "check then do". Consider what happens if two database connections try to do the same thing at roughly the same time.
Connection A | Connection B |
---|---|
if not exists... | |
if not exists... | |
true | |
true | |
insert... | |
insert... | |
success | |
duplicate |
To avoid this, "do then check". Do the insert and catch the duplicate key error.