protected RolloutGroupsValidation validateTargetsInGroups(final List<RolloutGroup> groups, final String baseFilter, final long totalTargets) { final List<Long> groupTargetCounts = new ArrayList<>(groups.size()); final Map<String, Long> targetFilterCounts = groups.stream() .map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct() .collect(Collectors.toMap(Function.identity(), targetManagement::countByRsql)); long unusedTargetsCount = 0; for (int i = 0; i < groups.size(); i++) { final RolloutGroup group = groups.get(i); final String groupTargetFilter = RolloutHelper.getGroupTargetFilter(baseFilter, group); RolloutHelper.verifyRolloutGroupTargetPercentage(group.getTargetPercentage()); final long targetsInGroupFilter = targetFilterCounts.get(groupTargetFilter); final long overlappingTargets = countOverlappingTargetsWithPreviousGroups(baseFilter, groups, group, i, targetFilterCounts); final long realTargetsInGroup; // Assume that targets which were not used in the previous groups // are used in this group if (overlappingTargets > 0 && unusedTargetsCount > 0) { realTargetsInGroup = targetsInGroupFilter - overlappingTargets + unusedTargetsCount; unusedTargetsCount = 0; } else { realTargetsInGroup = targetsInGroupFilter - overlappingTargets; } final long reducedTargetsInGroup = Math .round(group.getTargetPercentage() / 100 * (double) realTargetsInGroup); groupTargetCounts.add(reducedTargetsInGroup); unusedTargetsCount += realTargetsInGroup - reducedTargetsInGroup; } return new RolloutGroupsValidation(totalTargets, groupTargetCounts); }