Using oracle database.
Here’s how i think the SQLException happens…
Say i have two instances of a service running in parallel. Both of them do the following:
- Query cache(B) to see if Person exists there.
- If person exists, but out of date OR doesnt exist = do a query on the main database(A).
- If Person found in database (A) and NOT found earlier in cache (B). INSERT, else if person was found in cache earlier but was out of date UPDATE cache.
I use the following code to make the decision, based on earlier query to cache B.
void insertOrUpdate(RegistryPersonMo person) {
if (person.getId() == null) {
insertPerson(person);
} else {
updatePerson(person);
}
}
and insert using Spring JDBC:
void insertPerson(RegistryPersonMo person) {
Number id = insertInto("PERSON_REGISTRY", "RAAMAT").usingGeneratedKeyColumns("ID").executeAndReturnKey(usingParameters(person));
if (id != null) {
person.setId(id.longValue());
}
}
The actual problem occurs when two instances of the service have finished querying the cache(B) and the person wasn’t found (null). Then one instance does an INSERT, because data did not exist.The other gets SQLException upon trying to do the same, because an entry with a unique constraint already exists.
Does anyone know what the best\standard workaround is? Some ideas i’ve had:
- Lock reading of the row until insert done. Can i do this using Spring?
- Use replace or insert with ignore. still learning, are there any downsides to these ?
Bear in mind i’d like to use Spring and automate the query as much as possible..
I think it’s fine in this situation just to ignore the unique constraint exception. Yes, this is race condition but the expected one – desired outcome is achieved, record inserted. Perhaps log it to be able to assert how often this is happening.
Locking or transaction serialization would resolve this issue but won’t make much sense in this case, in my opinion.