I have an issue when two threads try to create an entity in my database. The first thread checks if the object exists, sees that it is not there, and then creates it. However, at the same time, the second thread also checks if the object exists and, not finding it, tries to create it as well.
I want to solve this issue using @Retryabl
e on my getOrCreateEntity
method because it seems to be the simplest solution (at least, I think so). This method is called from another method annotated with @Transactional
.
So, I have two questions:
@Transactional(propagation = Propagation.REQUIRES_NEW)
to my method with @Retryable
?DataIntegrityViolationException
) and, in the catch block, retrieve the entity that has already been created?Here is the code :
@Retryable(
retryFor = {DataIntegrityViolationException.class},
maxAttempts = 2,
backoff = @Backoff(delay = 100)
)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public T getOrCreateEntity(UUID entityUuid) {
T entity = findEntityByUuid(entityUuid);
if (entity != null) {
return entity;
}
try {
log.info("Creating entity with UUID {}", entityUuid);
T newEntity = createNewEntity(entityUuid);
return repository.save(newEntity);
} catch (DataIntegrityViolationException e) {
log.warn("Concurrent insert detected, returning existing entity");
return findEntityByUuid(entityUuid);
}
}
So, as I mentioned, getOrCreateEntity
is called by a method that already holds a transactional context.
Thanks in advance,
If you do } catch (DataIntegrityViolationException e) {
, then you don't need that @Retryable
, since in the catch
block you do that really manually.
The Propagation.REQUIRES_NEW
depends on your business logic. If it really requires that entity has to be there independently of the current transaction, then yes, you would need a new one just for this operation. Otherwise it might be OK to have such a retrieval as part of existing transaction. Yes, it might fail in the end because entity is already there by another transaction, but that might be OK as well according to the whole business logic of this call chain.