From de323b66d135061effc803aa66e5df6d20a38ef2 Mon Sep 17 00:00:00 2001 From: Avgustin Marinov Date: Fri, 4 Oct 2024 11:05:05 +0300 Subject: [PATCH] Fix concurrent starting the next group (#1853) when in StartNextGroupRolloutGroupSuccessAction#startNextGroup: 1. start all scheduled actions 2. if started are > 0 -> RUNNING, otherwise -> FINISHED (if not dynamic rollout) what could possibly happen is that at same time: * because of a success condition met the JpaRolloutsExecutor triggers start the group * user triggers start of the next group (via RolloutsManagement#triggerNextGroup) then it could: * the 'first' one succeeds to start next group * the second attempts to start it (JpaRolloutsExecutor found the previous had met the success condition or trigger next found it SCHEDULED and next to run) * the second finds no scheduled actions (just running) and decides there are no actions. So, it assumes (wrongly) no actions in group - and set it as FINISHED This way we could have FINISHED group with still running actions Signed-off-by: Marinov Avgustin --- .../repository/DeploymentManagement.java | 16 +---- .../repository/jpa/JpaRolloutExecutor.java | 32 ++++----- .../management/JpaDeploymentManagement.java | 69 ++++++------------- .../jpa/management/JpaRolloutManagement.java | 17 ++--- .../repository/RolloutGroupRepository.java | 2 +- ...artNextGroupRolloutGroupSuccessAction.java | 62 +++++------------ .../jpa/management/RolloutManagementTest.java | 5 +- 7 files changed, 65 insertions(+), 138 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java index dd47da3e8..adea1dd09 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java @@ -359,20 +359,6 @@ public interface DeploymentManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) Page findMessagesByActionStatusId(@NotNull Pageable pageable, long actionStatusId); - /** - * Counts all messages for an {@link ActionStatus}. - *

- * No access control applied. - * - * @deprecated Used by UI only. With future removal of UI it could be removed. - * @param actionStatusId - * the id of {@link ActionStatus} to count the messages from - * @return count of messages by a specific {@link ActionStatus} id - */ - @Deprecated(forRemoval = true) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - long countMessagesByActionStatusId(long actionStatusId); - /** * Get the {@link Action} entity for given actionId with all lazy attributes * (i.e. distributionSet, target, target.assignedDs). @@ -498,7 +484,7 @@ public interface DeploymentManagement { * @return the amount of started actions */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - long startScheduledActionsByRolloutGroupParent(long rolloutId, long distributionSetId, Long rolloutGroupParentId); + void startScheduledActionsByRolloutGroupParent(long rolloutId, long distributionSetId, Long rolloutGroupParentId); /** * Handles the target assignments. Shall be part of same group diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java index ac07cdf5d..48ff6d134 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java @@ -267,8 +267,7 @@ public class JpaRolloutExecutor implements RolloutExecutor { deleteScheduledActions(rollout, scheduledActions); // avoid another scheduler round and re-check if all scheduled actions - // has been cleaned up. we flush first to ensure that the we include the - // deletion above + // has been cleaned up. we flush first to ensure that will include the deletion above entityManager.flush(); final boolean hasScheduledActionsLeft = actionRepository.countByRolloutIdAndStatus(rollout.getId(), Status.SCHEDULED) > 0; @@ -354,21 +353,19 @@ public class JpaRolloutExecutor implements RolloutExecutor { } } - final List rolloutGroupsRunning = + final List runningGroups = rollout.getRolloutGroups().stream() .filter(group -> group.getStatus() == RolloutGroupStatus.RUNNING) .map(JpaRolloutGroup.class::cast) .toList(); - if (rolloutGroupsRunning.isEmpty()) { - // no running rollouts, probably there was an error - // somewhere at the latest group. And the latest group has - // been switched from running into error state. So we need - // to find the latest group which + if (runningGroups.isEmpty()) { + // no running rollouts, probably there was an error somewhere at the latest group. And the latest group has + // been switched from running into error state. So we need to find the latest group which executeLatestRolloutGroup(rollout); } else { - log.debug("Rollout {} has {} running groups", rollout.getId(), rolloutGroupsRunning.size()); - executeRolloutGroups(rollout, rolloutGroupsRunning, rollout.getRolloutGroups().get(rollout.getRolloutGroups().size() - 1)); + log.debug("Rollout {} has {} running groups", rollout.getId(), runningGroups.size()); + executeRunningGroups(rollout, runningGroups, rollout.getRolloutGroups().get(rollout.getRolloutGroups().size() - 1)); } if (isRolloutComplete(rollout)) { @@ -458,26 +455,24 @@ public class JpaRolloutExecutor implements RolloutExecutor { } } - private void executeRolloutGroups(final JpaRollout rollout, final List rolloutGroups, final RolloutGroup lastRolloutGroup) { - for (final JpaRolloutGroup rolloutGroup : rolloutGroups) { + private void executeRunningGroups(final JpaRollout rollout, final List runningGroups, final RolloutGroup lastGroup) { + for (final JpaRolloutGroup rolloutGroup : runningGroups) { final long targetCount = countTargetsFrom(rolloutGroup); if (rolloutGroup.getTotalTargets() != targetCount) { updateTotalTargetCount(rolloutGroup, targetCount); } - final RolloutGroup evalProxy = rolloutGroup == rolloutGroups.get(rolloutGroups.size() - 1) ? + final RolloutGroup evalProxy = rolloutGroup == runningGroups.get(runningGroups.size() - 1) ? evalProxy(rolloutGroup) : rolloutGroup; - // error state check, do we need to stop the whole - // rollout because of error? + // error state check, do we need to stop the whole rollout because of error? final boolean isError = checkErrorState(rollout, evalProxy); if (isError) { log.info("Rollout {} {} has error, calling error action", rollout.getName(), rollout.getId()); callErrorAction(rollout, rolloutGroup); } else { - // not in error so check finished state, do we need to - // start the next group? + // not in error so check finished state, do we need to start the next group? checkSuccessCondition(rollout, rolloutGroup, evalProxy, rolloutGroup.getSuccessCondition()); - if (!(rolloutGroup == lastRolloutGroup && rolloutGroup.isDynamic()) && isRolloutGroupComplete(rollout, rolloutGroup)) { + if (!(rolloutGroup == lastGroup && rolloutGroup.isDynamic()) && isRolloutGroupComplete(rollout, rolloutGroup)) { rolloutGroup.setStatus(RolloutGroupStatus.FINISHED); rolloutGroupRepository.save(rolloutGroup); } @@ -757,6 +752,7 @@ public class JpaRolloutExecutor implements RolloutExecutor { } return; } + final JpaRolloutGroup group = new JpaRolloutGroup(); final String lastGroupWithoutSuffix = "group-" + groupCount; final String suffix = lastGroup.getName().startsWith(lastGroupWithoutSuffix) ? lastGroup.getName().substring(lastGroupWithoutSuffix.length()) : ""; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java index a7a170578..69f9bb1b4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java @@ -645,40 +645,38 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl } @Override - public long startScheduledActionsByRolloutGroupParent(final long rolloutId, final long distributionSetId, + public void startScheduledActionsByRolloutGroupParent(final long rolloutId, final long distributionSetId, final Long rolloutGroupParentId) { - long totalActionsCount = 0L; - long lastStartedActionsCount; - do { - lastStartedActionsCount = DeploymentHelper.runInNewTransaction( + while (DeploymentHelper.runInNewTransaction( txManager, "startScheduledActions-" + rolloutId, status -> { - final Page rolloutGroupActions = findActionsByRolloutAndRolloutGroupParent( - rolloutId, rolloutGroupParentId, ACTION_PAGE_LIMIT); - - if (rolloutGroupActions.getContent().isEmpty()) { - return 0L; + final PageRequest pageRequest = PageRequest.of(0, ACTION_PAGE_LIMIT); + final Page groupScheduledActions; + if (rolloutGroupParentId == null) { + groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIsNullAndStatus(pageRequest, rolloutId, Action.Status.SCHEDULED); + } else { + groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIdAndStatus(pageRequest, rolloutId, rolloutGroupParentId, Action.Status.SCHEDULED); } - // self invocation won't check @PreAuthorize but it is already checked for the method - startScheduledActions(rolloutGroupActions.getContent()); - - return rolloutGroupActions.getTotalElements(); - }); - totalActionsCount += lastStartedActionsCount; - } while (lastStartedActionsCount > 0); - - return totalActionsCount; + if (groupScheduledActions.getContent().isEmpty()) { + return 0L; + } else { + // self invocation won't check @PreAuthorize but it is already checked for the method + startScheduledActions(groupScheduledActions.getContent()); + return groupScheduledActions.getTotalElements(); + } + }) > 0); } - @Override public void startScheduledActions(final List rolloutGroupActions) { // Close actions already assigned and collect pending assignments final List pendingTargetAssignments = rolloutGroupActions.stream() - .map(JpaAction.class::cast).map(this::closeActionIfSetWasAlreadyAssigned).filter(Objects::nonNull) - .collect(Collectors.toList()); + .map(JpaAction.class::cast) + .map(this::closeActionIfSetWasAlreadyAssigned) + .filter(Objects::nonNull) + .toList(); if (pendingTargetAssignments.isEmpty()) { return; } @@ -689,21 +687,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl } } - private Page findActionsByRolloutAndRolloutGroupParent(final Long rolloutId, - final Long rolloutGroupParentId, final int limit) { - - final PageRequest pageRequest = PageRequest.of(0, limit); - if (rolloutGroupParentId == null) { - return actionRepository.findByRolloutIdAndRolloutGroupParentIsNullAndStatus(pageRequest, rolloutId, - Action.Status.SCHEDULED); - } else { - return actionRepository.findByRolloutIdAndRolloutGroupParentIdAndStatus(pageRequest, rolloutId, - rolloutGroupParentId, Action.Status.SCHEDULED); - } - } - private JpaAction closeActionIfSetWasAlreadyAssigned(final JpaAction action) { - if (isMultiAssignmentsEnabled()) { return action; } @@ -899,19 +883,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl return new PageImpl<>(result, pageable, result.size()); } - @Override - public long countMessagesByActionStatusId(final long actionStatusId) { - final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - - final CriteriaQuery countMsgQuery = cb.createQuery(Long.class); - final Root countMsgQueryFrom = countMsgQuery.distinct(true).from(JpaActionStatus.class); - final ListJoin cJoin = countMsgQueryFrom.joinList("messages", JoinType.LEFT); - countMsgQuery.select(cb.count(cJoin)) - .where(cb.equal(countMsgQueryFrom.get(JpaActionStatus_.id), actionStatusId)); - - return entityManager.createQuery(countMsgQuery).getSingleResult(); - } - @Override public long countActionsAll() { return actionRepository.count(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java index 965fdad8d..ee4dcbc11 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java @@ -452,7 +452,7 @@ public class JpaRolloutManagement implements RolloutManagement { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision, final String remark) { log.debug("approveOrDeny rollout called for rollout {} with decision {}", rolloutId, decision); - final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId); + final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId); RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.WAITING_FOR_APPROVAL); switch (decision) { case APPROVED: @@ -478,7 +478,7 @@ public class JpaRolloutManagement implements RolloutManagement { public Rollout start(final long rolloutId) { log.debug("startRollout called for rollout {}", rolloutId); - final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId); + final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId); RolloutHelper.checkIfRolloutCanStarted(rollout, rollout); rollout.setStatus(RolloutStatus.STARTING); rollout.setLastCheck(0); @@ -490,7 +490,7 @@ public class JpaRolloutManagement implements RolloutManagement { @Retryable(include = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public void pauseRollout(final long rolloutId) { - final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId); + final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId); if (RolloutStatus.RUNNING != rollout.getStatus()) { throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is " + rollout.getStatus().name().toLowerCase()); @@ -509,7 +509,7 @@ public class JpaRolloutManagement implements RolloutManagement { @Retryable(include = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public void resumeRollout(final long rolloutId) { - final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId); + final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId); if (RolloutStatus.PAUSED != rollout.getStatus()) { throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is " + rollout.getStatus().name().toLowerCase()); @@ -583,7 +583,7 @@ public class JpaRolloutManagement implements RolloutManagement { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public Rollout update(final RolloutUpdate u) { final GenericRolloutUpdate update = (GenericRolloutUpdate) u; - final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(update.getId()); + final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(update.getId()); checkIfDeleted(update.getId(), rollout.getStatus()); @@ -598,7 +598,7 @@ public class JpaRolloutManagement implements RolloutManagement { } } - private JpaRollout getRolloutAndThrowExceptionIfNotFound(final Long rolloutId) { + private JpaRollout getRolloutOrThrowExceptionIfNotFound(final Long rolloutId) { return rolloutRepository.findById(rolloutId) .orElseThrow(() -> new EntityNotFoundException(Rollout.class, rolloutId)); } @@ -809,7 +809,7 @@ public class JpaRolloutManagement implements RolloutManagement { @Retryable(include = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public void triggerNextGroup(final long rolloutId) { - final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId); + final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId); if (RolloutStatus.RUNNING != rollout.getStatus()) { throw new RolloutIllegalStateException("Rollout is not in running state"); } @@ -824,7 +824,8 @@ public class JpaRolloutManagement implements RolloutManagement { final RolloutGroup latestRunning = groups.stream() .sorted(Comparator.comparingLong(RolloutGroup::getId).reversed()) - .filter(g -> RolloutGroupStatus.RUNNING.equals(g.getStatus())).findFirst() + .filter(g -> RolloutGroupStatus.RUNNING.equals(g.getStatus())) + .findFirst() .orElseThrow(() -> new RolloutIllegalStateException("No group is running")); startNextRolloutGroupAction.exec(rollout, latestRunning); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutGroupRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutGroupRepository.java index 6a28bc90e..51edd6b61 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutGroupRepository.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutGroupRepository.java @@ -121,7 +121,7 @@ public interface RolloutGroupRepository @Modifying @Transactional @Query("UPDATE JpaRolloutGroup g SET g.status = :status WHERE g.parent = :parent") - void setStatusForCildren(@Param("status") RolloutGroupStatus status, @Param("parent") RolloutGroup parent); + void setStatusForChildren(@Param("status") RolloutGroupStatus status, @Param("parent") RolloutGroup parent); /** * Retrieves all {@link RolloutGroup} for a specific rollout and status not diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java index 70297af42..88ed64309 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java @@ -9,12 +9,9 @@ */ package org.eclipse.hawkbit.repository.jpa.rollout.condition; -import java.util.List; - import lombok.extern.slf4j.Slf4j; import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository; -import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus; @@ -27,13 +24,12 @@ import org.eclipse.hawkbit.security.SystemSecurityContext; public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActionEvaluator { private final RolloutGroupRepository rolloutGroupRepository; - private final DeploymentManagement deploymentManagement; - private final SystemSecurityContext systemSecurityContext; - public StartNextGroupRolloutGroupSuccessAction(final RolloutGroupRepository rolloutGroupRepository, - final DeploymentManagement deploymentManagement, final SystemSecurityContext systemSecurityContext) { + public StartNextGroupRolloutGroupSuccessAction( + final RolloutGroupRepository rolloutGroupRepository, final DeploymentManagement deploymentManagement, + final SystemSecurityContext systemSecurityContext) { this.rolloutGroupRepository = rolloutGroupRepository; this.deploymentManagement = deploymentManagement; this.systemSecurityContext = systemSecurityContext; @@ -44,45 +40,25 @@ public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActi return RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP; } + // Note - the exec could be called by JpaRolloutsExecutor and buy JpaRolloutsManagement#triggerNextGroup + // this means it cold be called by concurrently. @Override public void exec(final Rollout rollout, final RolloutGroup rolloutGroup) { systemSecurityContext.runAsSystem(() -> { - startNextGroup(rollout, rolloutGroup); + // retrieve all actions according to the parent group of the finished rolloutGroup, + // so retrieve all child-group actions which need to be started. + deploymentManagement.startScheduledActionsByRolloutGroupParent( + rollout.getId(), rollout.getDistributionSet().getId(), rolloutGroup.getId()); + log.debug("Next actions started for rollout {} and parent group {}", rollout, rolloutGroup); + if (!rolloutGroupRepository + .findByParentIdAndStatus(rolloutGroup.getId(), RolloutGroupStatus.SCHEDULED).isEmpty()) { + // get next scheduled group and set them in state running + // there could be a case that the next group is empty (e.g. targets has been deleted after the group has been scheduled) + // but then, on the next run it will be finished and next will be started. + rolloutGroupRepository.setStatusForChildren(RolloutGroupStatus.RUNNING, rolloutGroup); + log.debug("Next group set to RUNNING for rollout {} and parent group {}", rollout, rolloutGroup); + } return null; }); } - - private void startNextGroup(final Rollout rollout, final RolloutGroup rolloutGroup) { - // retrieve all actions according to the parent group of the finished - // rolloutGroup, so retrieve all child-group actions which need to be - // started. - final long countOfStartedActions = deploymentManagement.startScheduledActionsByRolloutGroupParent( - rollout.getId(), rollout.getDistributionSet().getId(), rolloutGroup.getId()); - log.debug("{} Next actions started for rollout {} and parent group {}", countOfStartedActions, rollout, - rolloutGroup); - if (countOfStartedActions > 0) { - // get all next scheduled groups and set them in state running - rolloutGroupRepository.setStatusForCildren(RolloutGroupStatus.RUNNING, rolloutGroup); - } else { - log.debug("No actions to start for next rolloutgroup of parent {} {}", rolloutGroup.getId(), - rolloutGroup.getName()); - // nothing for next group, just finish the group, this can happen - // e.g. if targets has been deleted after the group has been - // scheduled. If the group is empty now, we just finish the group if - // there are not actions available for this group. - final List findByRolloutGroupParent = rolloutGroupRepository - .findByParentIdAndStatus(rolloutGroup.getId(), RolloutGroupStatus.SCHEDULED); - findByRolloutGroupParent.forEach(nextGroup -> { - if (nextGroup.isDynamic()) { - nextGroup.setStatus(RolloutGroupStatus.RUNNING); - } else { - log.debug("Rolloutgroup {} is finished, starting next group", nextGroup); - nextGroup.setStatus(RolloutGroupStatus.FINISHED); - rolloutGroupRepository.save(nextGroup); - // find the next group to set in running state - startNextGroup(rollout, nextGroup); - } - }); - } - } -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java index 9230032a7..6d87cfc1d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java @@ -374,15 +374,12 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { successCondition, errorCondition); finishActionAndDeleteTargetsOfFirstRunningGroup(createdRollout); - checkSecondGroupStatusIsRunning(createdRollout); finishActionAndDeleteTargetsOfSecondRunningGroup(createdRollout); - deleteAllTargetsFromThirdGroup(createdRollout); - + rolloutHandler.handleAll(); // one more time to finish the second group verifyRolloutAndAllGroupsAreFinished(createdRollout); - } @Step("Finish three actions of the rollout group and delete two targets")