Fix for endless loop when an exception of type EntityAlreadyExistsException is thrown in the context of the retryable findOrRegisterTargetIfItDoesNotExist method (#828)

* fixed typo in method name

Signed-off-by: Ahmed Sayed <ahmed.sayed@bosch-si.com>

* Added recover method to handle EntityAlreadyExistsException

Signed-off-by: Ahmed Sayed <ahmed.sayed@bosch-si.com>

* Added tests

Signed-off-by: Ahmed Sayed <ahmed.sayed@bosch-si.com>

* Apply suggestions from code review

Use thenThrow(Exception.class) instead of (new Exception())

Co-Authored-By: a-sayyed <ahmed.sayed@bosch-si.com>

* Apply suggestions from code review

use final modifier

Co-Authored-By: a-sayyed <ahmed.sayed@bosch-si.com>

* Adapted review findings

Signed-off-by: Ahmed Sayed <ahmed.sayed@bosch-si.com>

* Adapted review findings

Signed-off-by: Ahmed Sayed <ahmed.sayed@bosch-si.com>

* added logs for EntityAlreadyExistsException case

Signed-off-by: Ahmed Sayed <ahmed.sayed@bosch-si.com>
This commit is contained in:
Ahmed Sayed
2019-04-30 12:47:06 +02:00
committed by Dominic Schabel
parent ed95ae6398
commit 9884452ad4
10 changed files with 137 additions and 26 deletions

View File

@@ -50,6 +50,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEven
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidTargetAttributeException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate;
@@ -364,16 +365,19 @@ public class JpaControllerManagement implements ControllerManagement {
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target findOrRegisterTargetIfItDoesNotexist(final String controllerId, final URI address) {
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class,
maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address) {
final Specification<JpaTarget> spec = (targetRoot, query, cb) -> cb
.equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
final Optional<JpaTarget> target = targetRepository.findOne(spec);
return targetRepository.findOne(spec).map(target -> updateTargetStatus(target, address))
.orElseGet(() -> createTarget(controllerId, address));
}
if (!target.isPresent()) {
private Target createTarget(final String controllerId, final URI address) {
try {
final Target result = targetRepository.save((JpaTarget) entityFactory.target().create()
.controllerId(controllerId).description("Plug and Play target: " + controllerId).name(controllerId)
.status(TargetUpdateStatus.REGISTERED).lastTargetQuery(System.currentTimeMillis())
@@ -382,9 +386,11 @@ public class JpaControllerManagement implements ControllerManagement {
afterCommit.afterCommit(() -> eventPublisher.publishEvent(new TargetPollEvent(result, bus.getId())));
return result;
} catch (final EntityAlreadyExistsException e){
LOG.warn("Caught an EntityAlreadyExistsException while creating non existing target " +
"[controllerId:{}, address:{}, tenant: {}]", controllerId, address, tenantAware.getCurrentTenant());
throw e;
}
return updateTargetStatus(target.get(), address);
}
/**
@@ -472,7 +478,7 @@ public class JpaControllerManagement implements ControllerManagement {
/**
* Stores target directly to DB in case either {@link Target#getAddress()}
* or {@link Target#getUpdateStatus()} changes or the buffer queue is full.
*
*
*/
private Target updateTargetStatus(final JpaTarget toUpdate, final URI address) {
boolean storeEager = isStoreEager(toUpdate, address);
@@ -1029,4 +1035,9 @@ public class JpaControllerManagement implements ControllerManagement {
afterCommit.afterCommit(
() -> eventPublisher.publishEvent(new CancelTargetAssignmentEvent(target, actionId, bus.getId())));
}
// for testing
void setTargetRepository(final TargetRepository targetRepositorySpy) {
this.targetRepository = targetRepositorySpy;
}
}