diff options
Diffstat (limited to 'cps-ri/src')
11 files changed, 277 insertions, 451 deletions
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java index f634008dc6..4756991138 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java @@ -25,6 +25,7 @@ package org.onap.cps.spi.impl; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; +import io.micrometer.core.annotation.Timed; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -80,88 +81,83 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService private final JsonObjectMapper jsonObjectMapper; private final SessionManager sessionManager; - private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})"; + private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?])?)"; @Override public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentNodeXpath, final DataNode newChildDataNode) { - addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChildDataNode); + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + addNewChildDataNode(anchorEntity, parentNodeXpath, newChildDataNode); } @Override public void addChildDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<DataNode> dataNodes) { - addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodes); + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + addChildrenDataNodes(anchorEntity, parentNodeXpath, dataNodes); } @Override public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<DataNode> newListElements) { - addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newListElements); + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + addChildrenDataNodes(anchorEntity, parentNodeXpath, newListElements); } @Override public void addMultipleLists(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<Collection<DataNode>> newLists) { + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); final Collection<String> failedXpaths = new HashSet<>(); - newLists.forEach(newList -> { + for (final Collection<DataNode> newList : newLists) { try { - addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newList); + addChildrenDataNodes(anchorEntity, parentNodeXpath, newList); } catch (final AlreadyDefinedExceptionBatch e) { failedXpaths.addAll(e.getAlreadyDefinedXpaths()); } - }); - + } if (!failedXpaths.isEmpty()) { throw new AlreadyDefinedExceptionBatch(failedXpaths); } - } - private void addNewChildDataNode(final String dataspaceName, final String anchorName, - final String parentNodeXpath, final DataNode newChild) { - final FragmentEntity parentFragmentEntity = - getFragmentEntity(dataspaceName, anchorName, parentNodeXpath); - final FragmentEntity newChildAsFragmentEntity = - convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(), - parentFragmentEntity.getAnchor(), newChild); + private void addNewChildDataNode(final AnchorEntity anchorEntity, final String parentNodeXpath, + final DataNode newChild) { + final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); + final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, newChild); newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); try { fragmentRepository.save(newChildAsFragmentEntity); } catch (final DataIntegrityViolationException e) { - throw AlreadyDefinedException.forDataNode(newChild.getXpath(), anchorName, e); + throw AlreadyDefinedException.forDataNode(newChild.getXpath(), anchorEntity.getName(), e); } - } - private void addChildrenDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath, + private void addChildrenDataNodes(final AnchorEntity anchorEntity, final String parentNodeXpath, final Collection<DataNode> newChildren) { - final FragmentEntity parentFragmentEntity = - getFragmentEntity(dataspaceName, anchorName, parentNodeXpath); + final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size()); try { - newChildren.forEach(newChildAsDataNode -> { + for (final DataNode newChildAsDataNode : newChildren) { final FragmentEntity newChildAsFragmentEntity = - convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(), - parentFragmentEntity.getAnchor(), newChildAsDataNode); + convertToFragmentWithAllDescendants(anchorEntity, newChildAsDataNode); newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); fragmentEntities.add(newChildAsFragmentEntity); - }); + } fragmentRepository.saveAll(fragmentEntities); } catch (final DataIntegrityViolationException e) { log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations", e, fragmentEntities.size()); - retrySavingEachChildIndividually(dataspaceName, anchorName, parentNodeXpath, newChildren); + retrySavingEachChildIndividually(anchorEntity, parentNodeXpath, newChildren); } } - private void retrySavingEachChildIndividually(final String dataspaceName, final String anchorName, - final String parentNodeXpath, + private void retrySavingEachChildIndividually(final AnchorEntity anchorEntity, final String parentNodeXpath, final Collection<DataNode> newChildren) { final Collection<String> failedXpaths = new HashSet<>(); for (final DataNode newChild : newChildren) { try { - addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChild); + addNewChildDataNode(anchorEntity, parentNodeXpath, newChild); } catch (final AlreadyDefinedException e) { failedXpaths.add(newChild.getXpath()); } @@ -179,32 +175,26 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Override public void storeDataNodes(final String dataspaceName, final String anchorName, final Collection<DataNode> dataNodes) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); final List<FragmentEntity> fragmentEntities = new ArrayList<>(dataNodes.size()); try { for (final DataNode dataNode: dataNodes) { - final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity, - dataNode); + final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, dataNode); fragmentEntities.add(fragmentEntity); } fragmentRepository.saveAll(fragmentEntities); } catch (final DataIntegrityViolationException exception) { log.warn("Exception occurred : {} , While saving : {} data nodes, Retrying saving data nodes individually", exception, dataNodes.size()); - storeDataNodesIndividually(dataspaceName, anchorName, dataNodes); + storeDataNodesIndividually(anchorEntity, dataNodes); } } - private void storeDataNodesIndividually(final String dataspaceName, final String anchorName, - final Collection<DataNode> dataNodes) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); + private void storeDataNodesIndividually(final AnchorEntity anchorEntity, final Collection<DataNode> dataNodes) { final Collection<String> failedXpaths = new HashSet<>(); for (final DataNode dataNode: dataNodes) { try { - final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity, - dataNode); + final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, dataNode); fragmentRepository.save(fragmentEntity); } catch (final DataIntegrityViolationException e) { failedXpaths.add(dataNode.getXpath()); @@ -219,30 +209,25 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService * Convert DataNode object into Fragment and places the result in the fragments placeholder. Performs same action * for all DataNode children recursively. * - * @param dataspaceEntity dataspace * @param anchorEntity anchorEntity * @param dataNodeToBeConverted dataNode * @return a Fragment built from current DataNode */ - private FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity, - final AnchorEntity anchorEntity, + private FragmentEntity convertToFragmentWithAllDescendants(final AnchorEntity anchorEntity, final DataNode dataNodeToBeConverted) { - final FragmentEntity parentFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNodeToBeConverted); + final FragmentEntity parentFragment = toFragmentEntity(anchorEntity, dataNodeToBeConverted); final Builder<FragmentEntity> childFragmentsImmutableSetBuilder = ImmutableSet.builder(); for (final DataNode childDataNode : dataNodeToBeConverted.getChildDataNodes()) { - final FragmentEntity childFragment = - convertToFragmentWithAllDescendants(parentFragment.getDataspace(), parentFragment.getAnchor(), - childDataNode); + final FragmentEntity childFragment = convertToFragmentWithAllDescendants(anchorEntity, childDataNode); childFragmentsImmutableSetBuilder.add(childFragment); } parentFragment.setChildFragments(childFragmentsImmutableSetBuilder.build()); return parentFragment; } - private FragmentEntity toFragmentEntity(final DataspaceEntity dataspaceEntity, - final AnchorEntity anchorEntity, final DataNode dataNode) { + private FragmentEntity toFragmentEntity(final AnchorEntity anchorEntity, final DataNode dataNode) { return FragmentEntity.builder() - .dataspace(dataspaceEntity) + .dataspace(anchorEntity.getDataspace()) .anchor(anchorEntity) .xpath(dataNode.getXpath()) .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves())) @@ -250,10 +235,12 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } @Override + @Timed(value = "cps.data.persistence.service.datanode.get", + description = "Time taken to get a data node") public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName, final String xpath, final FetchDescendantsOption fetchDescendantsOption) { - final String targetXpath = isRootXpath(xpath) ? xpath : CpsPathUtil.getNormalizedXpath(xpath); + final String targetXpath = getNormalizedXpath(xpath); final Collection<DataNode> dataNodes = getDataNodesForMultipleXpaths(dataspaceName, anchorName, Collections.singletonList(targetXpath), fetchDescendantsOption); if (dataNodes.isEmpty()) { @@ -263,18 +250,20 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } @Override + @Timed(value = "cps.data.persistence.service.datanode.batch.get", + description = "Time taken to get data nodes") public Collection<DataNode> getDataNodesForMultipleXpaths(final String dataspaceName, final String anchorName, final Collection<String> xpaths, final FetchDescendantsOption fetchDescendantsOption) { - final Collection<FragmentEntity> fragmentEntities = getFragmentEntities(dataspaceName, anchorName, xpaths); + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + final Collection<FragmentEntity> fragmentEntities = + getFragmentEntities(anchorEntity, xpaths, fetchDescendantsOption); return toDataNodes(fragmentEntities, fetchDescendantsOption); } - private Collection<FragmentEntity> getFragmentEntities(final String dataspaceName, final String anchorName, - final Collection<String> xpaths) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); - + private Collection<FragmentEntity> getFragmentEntities(final AnchorEntity anchorEntity, + final Collection<String> xpaths, + final FetchDescendantsOption fetchDescendantsOption) { final Collection<String> nonRootXpaths = new HashSet<>(xpaths); final boolean haveRootXpath = nonRootXpaths.removeIf(CpsDataPersistenceServiceImpl::isRootXpath); @@ -286,54 +275,45 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService log.warn("Error parsing xpath \"{}\": {}", xpath, e.getMessage()); } } - final Collection<FragmentEntity> fragmentEntities = - new HashSet<>(fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorEntity.getId(), normalizedXpaths)); - if (haveRootXpath) { - final List<FragmentExtract> fragmentExtracts = fragmentRepository.getTopLevelFragments(dataspaceEntity, - anchorEntity); - fragmentEntities.addAll(FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts)); + normalizedXpaths.addAll(fragmentRepository.findAllXpathByAnchorAndParentIdIsNull(anchorEntity)); } - return fragmentEntities; + final List<FragmentExtract> fragmentExtracts = + fragmentRepository.findExtractsWithDescendants(anchorEntity.getId(), normalizedXpaths, + fetchDescendantsOption.getDepth()); + + return FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts); } - private FragmentEntity getFragmentEntity(final String dataspaceName, final String anchorName, final String xpath) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); + private FragmentEntity getFragmentEntity(final AnchorEntity anchorEntity, final String xpath) { final FragmentEntity fragmentEntity; if (isRootXpath(xpath)) { - final List<FragmentExtract> fragmentExtracts = fragmentRepository.getTopLevelFragments(dataspaceEntity, - anchorEntity); + final List<FragmentExtract> fragmentExtracts = fragmentRepository.findAllExtractsByAnchor(anchorEntity); fragmentEntity = FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts) .stream().findFirst().orElse(null); } else { - final String normalizedXpath = getNormalizedXpath(xpath); - fragmentEntity = - fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, normalizedXpath); + fragmentEntity = fragmentRepository.getByAnchorAndXpath(anchorEntity, getNormalizedXpath(xpath)); } if (fragmentEntity == null) { - throw new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName(), xpath); + throw new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath); } return fragmentEntity; - } private Collection<FragmentEntity> buildFragmentEntitiesFromFragmentExtracts(final AnchorEntity anchorEntity, final String normalizedXpath) { final List<FragmentExtract> fragmentExtracts = - fragmentRepository.findByAnchorIdAndParentXpath(anchorEntity.getId(), normalizedXpath); - log.debug("Fetched {} fragment entities by anchor {} and cps path {}.", - fragmentExtracts.size(), anchorEntity.getName(), normalizedXpath); + fragmentRepository.findByAnchorAndParentXpath(anchorEntity, normalizedXpath); return FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts); - } @Override + @Timed(value = "cps.data.persistence.service.datanode.query", + description = "Time taken to query data nodes") public List<DataNode> queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath, final FetchDescendantsOption fetchDescendantsOption) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); final CpsPathQuery cpsPathQuery; try { cpsPathQuery = CpsPathUtil.getCpsPathQuery(cpsPath); @@ -347,7 +327,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } fragmentEntities = fragmentRepository.findByAnchorAndCpsPath(anchorEntity.getId(), cpsPathQuery); if (cpsPathQuery.hasAncestorAxis()) { - fragmentEntities = getAncestorFragmentEntities(anchorEntity.getId(), cpsPathQuery, fragmentEntities); + final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery); + fragmentEntities = getFragmentEntities(anchorEntity, ancestorXpaths, fetchDescendantsOption); } return createDataNodesFromProxiedFragmentEntities(fetchDescendantsOption, anchorEntity, fragmentEntities); } @@ -368,19 +349,12 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService fragmentRepository.quickFindWithDescendants(anchorEntity.getId(), xpathRegex); fragmentEntities = FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts); if (cpsPathQuery.hasAncestorAxis()) { - fragmentEntities = getAncestorFragmentEntities(anchorEntity.getId(), cpsPathQuery, fragmentEntities); + final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery); + fragmentEntities = getFragmentEntities(anchorEntity, ancestorXpaths, fetchDescendantsOption); } return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); } - private Collection<FragmentEntity> getAncestorFragmentEntities(final int anchorId, - final CpsPathQuery cpsPathQuery, - final Collection<FragmentEntity> fragmentEntities) { - final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery); - return ancestorXpaths.isEmpty() ? Collections.emptyList() - : fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorId, ancestorXpaths); - } - private List<DataNode> createDataNodesFromProxiedFragmentEntities( final FetchDescendantsOption fetchDescendantsOption, final AnchorEntity anchorEntity, @@ -411,13 +385,14 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } private static String getNormalizedXpath(final String xpathSource) { - final String normalizedXpath; + if (isRootXpath(xpathSource)) { + return xpathSource; + } try { - normalizedXpath = CpsPathUtil.getNormalizedXpath(xpathSource); + return CpsPathUtil.getNormalizedXpath(xpathSource); } catch (final PathParsingException e) { throw new CpsPathException(e.getMessage()); } - return normalizedXpath; } @Override @@ -440,8 +415,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final CpsPathQuery cpsPathQuery) { final Set<String> ancestorXpath = new HashSet<>(); final Pattern pattern = - Pattern.compile("([\\s\\S]*\\/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier()) - + REG_EX_FOR_OPTIONAL_LIST_INDEX + "\\/[\\s\\S]*"); + Pattern.compile("([\\s\\S]*/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier()) + + REG_EX_FOR_OPTIONAL_LIST_INDEX + "/[\\s\\S]*"); for (final FragmentEntity fragmentEntity : fragmentEntities) { final Matcher matcher = pattern.matcher(fragmentEntity.getXpath()); if (matcher.matches()) { @@ -486,7 +461,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Override public void updateDataLeaves(final String dataspaceName, final String anchorName, final String xpath, final Map<String, Serializable> updateLeaves) { - final FragmentEntity fragmentEntity = getFragmentEntity(dataspaceName, anchorName, xpath); + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + final FragmentEntity fragmentEntity = getFragmentEntity(anchorEntity, xpath); final String currentLeavesAsString = fragmentEntity.getAttributes(); final String mergedLeaves = mergeLeaves(updateLeaves, currentLeavesAsString); fragmentEntity.setAttributes(mergedLeaves); @@ -496,7 +472,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Override public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName, final DataNode dataNode) { - final FragmentEntity fragmentEntity = getFragmentEntity(dataspaceName, anchorName, dataNode.getXpath()); + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + final FragmentEntity fragmentEntity = getFragmentEntity(anchorEntity, dataNode.getXpath()); updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode); try { fragmentRepository.save(fragmentEntity); @@ -509,13 +486,15 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Override public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName, - final List<DataNode> updatedDataNodes) { + final Collection<DataNode> updatedDataNodes) { + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + final Map<String, DataNode> xpathToUpdatedDataNode = updatedDataNodes.stream() .collect(Collectors.toMap(DataNode::getXpath, dataNode -> dataNode)); final Collection<String> xpaths = xpathToUpdatedDataNode.keySet(); final Collection<FragmentEntity> existingFragmentEntities = - getFragmentEntities(dataspaceName, anchorName, xpaths); + getFragmentEntities(anchorEntity, xpaths, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); for (final FragmentEntity existingFragmentEntity : existingFragmentEntities) { final DataNode updatedDataNode = xpathToUpdatedDataNode.get(existingFragmentEntity.getXpath()); @@ -525,45 +504,41 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService try { fragmentRepository.saveAll(existingFragmentEntities); } catch (final StaleStateException staleStateException) { - retryUpdateDataNodesIndividually(dataspaceName, anchorName, existingFragmentEntities); + retryUpdateDataNodesIndividually(anchorEntity, existingFragmentEntities); } } - private void retryUpdateDataNodesIndividually(final String dataspaceName, final String anchorName, + private void retryUpdateDataNodesIndividually(final AnchorEntity anchorEntity, final Collection<FragmentEntity> fragmentEntities) { final Collection<String> failedXpaths = new HashSet<>(); - - fragmentEntities.forEach(dataNodeFragment -> { + for (final FragmentEntity dataNodeFragment : fragmentEntities) { try { fragmentRepository.save(dataNodeFragment); } catch (final StaleStateException e) { failedXpaths.add(dataNodeFragment.getXpath()); } - }); - + } if (!failedXpaths.isEmpty()) { final String failedXpathsConcatenated = String.join(",", failedXpaths); throw new ConcurrencyException("Concurrent Transactions", String.format( "DataNodes : %s in Dataspace :'%s' with Anchor : '%s' are updated by another transaction.", - failedXpathsConcatenated, dataspaceName, anchorName)); + failedXpathsConcatenated, anchorEntity.getDataspace().getName(), anchorEntity.getName())); } } private void updateFragmentEntityAndDescendantsWithDataNode(final FragmentEntity existingFragmentEntity, final DataNode newDataNode) { - existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves())); final Map<String, FragmentEntity> existingChildrenByXpath = existingFragmentEntity.getChildFragments().stream() .collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity)); final Collection<FragmentEntity> updatedChildFragments = new HashSet<>(); - for (final DataNode newDataNodeChild : newDataNode.getChildDataNodes()) { final FragmentEntity childFragment; if (isNewDataNode(newDataNodeChild, existingChildrenByXpath)) { - childFragment = convertToFragmentWithAllDescendants( - existingFragmentEntity.getDataspace(), existingFragmentEntity.getAnchor(), newDataNodeChild); + childFragment = convertToFragmentWithAllDescendants(existingFragmentEntity.getAnchor(), + newDataNodeChild); } else { childFragment = existingChildrenByXpath.get(newDataNodeChild.getXpath()); updateFragmentEntityAndDescendantsWithDataNode(childFragment, newDataNodeChild); @@ -579,18 +554,18 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Transactional public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<DataNode> newListElements) { - final FragmentEntity parentEntity = getFragmentEntity(dataspaceName, anchorName, parentNodeXpath); + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + final FragmentEntity parentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); final String listElementXpathPrefix = getListElementXpathPrefix(newListElements); final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath = extractListElementFragmentEntitiesByXPath(parentEntity.getChildFragments(), listElementXpathPrefix); - deleteListElements(parentEntity.getChildFragments(), existingListElementFragmentEntitiesByXPath); + parentEntity.getChildFragments().removeAll(existingListElementFragmentEntitiesByXPath.values()); final Set<FragmentEntity> updatedChildFragmentEntities = new HashSet<>(); for (final DataNode newListElement : newListElements) { final FragmentEntity existingListElementEntity = existingListElementFragmentEntitiesByXPath.get(newListElement.getXpath()); final FragmentEntity entityToBeAdded = getFragmentForReplacement(parentEntity, newListElement, existingListElementEntity); - updatedChildFragmentEntities.add(entityToBeAdded); } parentEntity.getChildFragments().addAll(updatedChildFragmentEntities); @@ -602,8 +577,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService public void deleteDataNodes(final String dataspaceName, final String anchorName) { final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); anchorRepository.findByDataspaceAndName(dataspaceEntity, anchorName) - .ifPresent( - anchorEntity -> fragmentRepository.deleteByAnchorIn(Set.of(anchorEntity))); + .ifPresent(anchorEntity -> fragmentRepository.deleteByAnchorIn(Collections.singletonList(anchorEntity))); } @Override @@ -619,21 +593,37 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Transactional public void deleteDataNodes(final String dataspaceName, final String anchorName, final Collection<String> xpathsToDelete) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); + deleteDataNodes(dataspaceName, anchorName, xpathsToDelete, false); + } + + private void deleteDataNodes(final String dataspaceName, final String anchorName, + final Collection<String> xpathsToDelete, final boolean onlySupportListDeletion) { + final boolean haveRootXpath = xpathsToDelete.stream().anyMatch(CpsDataPersistenceServiceImpl::isRootXpath); + if (haveRootXpath) { + deleteDataNodes(dataspaceName, anchorName); + return; + } + + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); final Collection<String> deleteChecklist = new HashSet<>(xpathsToDelete.size()); for (final String xpath : xpathsToDelete) { try { deleteChecklist.add(CpsPathUtil.getNormalizedXpath(xpath)); } catch (final PathParsingException e) { - log.debug("Error parsing xpath \"{}\": {}", xpath, e.getMessage()); + log.warn("Error parsing xpath \"{}\": {}", xpath, e.getMessage()); } } final Collection<String> xpathsToExistingContainers = fragmentRepository.findAllXpathByAnchorAndXpathIn(anchorEntity, deleteChecklist); - deleteChecklist.removeAll(xpathsToExistingContainers); + if (onlySupportListDeletion) { + final Collection<String> xpathsToExistingListElements = xpathsToExistingContainers.stream() + .filter(CpsPathUtil::isPathToListElement).collect(Collectors.toList()); + deleteChecklist.removeAll(xpathsToExistingListElements); + } else { + deleteChecklist.removeAll(xpathsToExistingContainers); + } final Collection<String> xpathsToExistingLists = deleteChecklist.stream() .filter(xpath -> fragmentRepository.existsByAnchorAndXpathStartsWith(anchorEntity, xpath + "[")) @@ -663,66 +653,13 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService private void deleteDataNode(final String dataspaceName, final String anchorName, final String targetXpath, final boolean onlySupportListNodeDeletion) { - final String parentNodeXpath; - FragmentEntity parentFragmentEntity = null; - boolean targetDeleted; - if (isRootXpath(targetXpath)) { - deleteDataNodes(dataspaceName, anchorName); - targetDeleted = true; - } else { - if (isRootContainerNodeXpath(targetXpath)) { - parentNodeXpath = targetXpath; - } else { - parentNodeXpath = CpsPathUtil.getNormalizedParentXpath(targetXpath); - } - parentFragmentEntity = getFragmentEntity(dataspaceName, anchorName, parentNodeXpath); - if (CpsPathUtil.isPathToListElement(targetXpath)) { - targetDeleted = deleteDataNode(parentFragmentEntity, targetXpath); - } else { - targetDeleted = deleteAllListElements(parentFragmentEntity, targetXpath); - final boolean tryToDeleteDataNode = !targetDeleted && !onlySupportListNodeDeletion; - if (tryToDeleteDataNode) { - targetDeleted = deleteDataNode(parentFragmentEntity, targetXpath); - } - } - } - if (!targetDeleted) { - final String additionalInformation = onlySupportListNodeDeletion - ? "The target is probably not a List." : ""; - throw new DataNodeNotFoundException(parentFragmentEntity.getDataspace().getName(), - parentFragmentEntity.getAnchor().getName(), targetXpath, additionalInformation); - } - } - - private boolean deleteDataNode(final FragmentEntity parentFragmentEntity, final String targetXpath) { - final String normalizedTargetXpath = CpsPathUtil.getNormalizedXpath(targetXpath); - if (parentFragmentEntity.getXpath().equals(normalizedTargetXpath)) { - fragmentRepository.deleteFragmentEntity(parentFragmentEntity.getId()); - return true; - } - if (parentFragmentEntity.getChildFragments() - .removeIf(fragment -> fragment.getXpath().equals(normalizedTargetXpath))) { - fragmentRepository.save(parentFragmentEntity); - return true; - } - return false; - } - - private boolean deleteAllListElements(final FragmentEntity parentFragmentEntity, final String listXpath) { - final String normalizedListXpath = CpsPathUtil.getNormalizedXpath(listXpath); - final String deleteTargetXpathPrefix = normalizedListXpath + "["; - if (parentFragmentEntity.getChildFragments() - .removeIf(fragment -> fragment.getXpath().startsWith(deleteTargetXpathPrefix))) { - fragmentRepository.save(parentFragmentEntity); - return true; + final String normalizedXpath = getNormalizedXpath(targetXpath); + try { + deleteDataNodes(dataspaceName, anchorName, Collections.singletonList(normalizedXpath), + onlySupportListNodeDeletion); + } catch (final DataNodeNotFoundExceptionBatch dataNodeNotFoundExceptionBatch) { + throw new DataNodeNotFoundException(dataspaceName, anchorName, targetXpath); } - return false; - } - - private static void deleteListElements( - final Collection<FragmentEntity> fragmentEntities, - final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath) { - fragmentEntities.removeAll(existingListElementFragmentEntitiesByXPath.values()); } private static String getListElementXpathPrefix(final Collection<DataNode> newListElements) { @@ -738,8 +675,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final DataNode newListElement, final FragmentEntity existingListElementEntity) { if (existingListElementEntity == null) { - return convertToFragmentWithAllDescendants( - parentEntity.getDataspace(), parentEntity.getAnchor(), newListElement); + return convertToFragmentWithAllDescendants(parentEntity.getAnchor(), newListElement); } if (newListElement.getChildDataNodes().isEmpty()) { copyAttributesFromNewListElement(existingListElementEntity, newListElement); @@ -755,10 +691,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return !existingListElementsByXpath.containsKey(replacementDataNode.getXpath()); } - private static boolean isRootContainerNodeXpath(final String xpath) { - return 0 == xpath.lastIndexOf('/'); - } - private void copyAttributesFromNewListElement(final FragmentEntity existingListElementEntity, final DataNode newListElement) { final FragmentEntity replacementFragmentEntity = @@ -787,4 +719,9 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } return jsonObjectMapper.asJsonString(currentLeavesAsMap); } + + private AnchorEntity getAnchorEntity(final String dataspaceName, final String anchorName) { + final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); + return anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); + } } diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java index 51ebcb4127..426a4601c7 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java @@ -25,10 +25,7 @@ package org.onap.cps.spi.repository; import java.util.Collection;
import java.util.List;
import java.util.Optional;
-import javax.validation.constraints.NotNull;
-import org.checkerframework.checker.nullness.qual.NonNull;
import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
import org.onap.cps.spi.entities.FragmentEntity;
import org.onap.cps.spi.entities.FragmentExtract;
import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
@@ -40,58 +37,28 @@ import org.springframework.stereotype.Repository; @Repository
public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, FragmentRepositoryCpsPathQuery,
- FragmentRepositoryMultiPathQuery, FragmentNativeRepository {
+ FragmentNativeRepository {
- Optional<FragmentEntity> findByDataspaceAndAnchorAndXpath(@NonNull DataspaceEntity dataspaceEntity,
- @NonNull AnchorEntity anchorEntity,
- @NonNull String xpath);
+ Optional<FragmentEntity> findByAnchorAndXpath(AnchorEntity anchorEntity, String xpath);
- default FragmentEntity getByDataspaceAndAnchorAndXpath(@NonNull DataspaceEntity dataspaceEntity,
- @NonNull AnchorEntity anchorEntity,
- @NonNull String xpath) {
- return findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, xpath)
- .orElseThrow(() -> new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName(), xpath));
+ default FragmentEntity getByAnchorAndXpath(final AnchorEntity anchorEntity, final String xpath) {
+ return findByAnchorAndXpath(anchorEntity, xpath).orElseThrow(() ->
+ new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath));
}
- @Query(
- value = "SELECT * FROM FRAGMENT WHERE anchor_id = :anchor AND dataspace_id = :dataspace AND parent_id is NULL",
- nativeQuery = true)
- List<FragmentEntity> findRootsByDataspaceAndAnchor(@Param("dataspace") int dataspaceId,
- @Param("anchor") int anchorId);
+ boolean existsByAnchorId(int anchorId);
- @Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"
- + " CAST(attributes AS TEXT) AS attributes"
- + " FROM FRAGMENT WHERE anchor_id = :anchorId",
- nativeQuery = true)
- List<FragmentExtract> findRootsByAnchorId(@Param("anchorId") int anchorId);
-
- /**
- * find top level fragment by anchor.
- *
- * @param dataspaceEntity dataspace entity
- * @param anchorEntity anchor entity
- * @return FragmentEntity fragment entity
- */
- default List<FragmentExtract> getTopLevelFragments(DataspaceEntity dataspaceEntity,
- AnchorEntity anchorEntity) {
- final List<FragmentExtract> fragmentExtracts = findRootsByAnchorId(anchorEntity.getId());
- if (fragmentExtracts.isEmpty()) {
- throw new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName());
- }
- return fragmentExtracts;
- }
+ @Query("SELECT f FROM FragmentEntity f WHERE anchor = :anchor")
+ List<FragmentExtract> findAllExtractsByAnchor(@Param("anchor") AnchorEntity anchorEntity);
@Modifying
- @Query("DELETE FROM FragmentEntity fe WHERE fe.anchor IN (:anchors)")
- void deleteByAnchorIn(@NotNull @Param("anchors") Collection<AnchorEntity> anchorEntities);
+ @Query("DELETE FROM FragmentEntity WHERE anchor IN (:anchors)")
+ void deleteByAnchorIn(@Param("anchors") Collection<AnchorEntity> anchorEntities);
- @Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"
- + " CAST(attributes AS TEXT) AS attributes"
- + " FROM FRAGMENT WHERE anchor_id = :anchorId"
- + " AND ( xpath = :parentXpath OR xpath LIKE CONCAT(:parentXpath,'/%') )",
- nativeQuery = true)
- List<FragmentExtract> findByAnchorIdAndParentXpath(@Param("anchorId") int anchorId,
- @Param("parentXpath") String parentXpath);
+ @Query("SELECT f FROM FragmentEntity f WHERE anchor = :anchor"
+ + " AND (xpath = :parentXpath OR xpath LIKE CONCAT(:parentXpath,'/%'))")
+ List<FragmentExtract> findByAnchorAndParentXpath(@Param("anchor") AnchorEntity anchorEntity,
+ @Param("parentXpath") String parentXpath);
@Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"
+ " CAST(attributes AS TEXT) AS attributes"
@@ -101,9 +68,30 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, List<FragmentExtract> quickFindWithDescendants(@Param("anchorId") int anchorId,
@Param("xpathRegex") String xpathRegex);
- @Query("SELECT f.xpath FROM FragmentEntity f WHERE f.anchor = :anchor AND f.xpath IN :xpaths")
+ @Query("SELECT xpath FROM FragmentEntity WHERE anchor = :anchor AND xpath IN :xpaths")
List<String> findAllXpathByAnchorAndXpathIn(@Param("anchor") AnchorEntity anchorEntity,
@Param("xpaths") Collection<String> xpaths);
boolean existsByAnchorAndXpathStartsWith(AnchorEntity anchorEntity, String xpath);
+
+ @Query("SELECT xpath FROM FragmentEntity WHERE anchor = :anchor AND parentId IS NULL")
+ List<String> findAllXpathByAnchorAndParentIdIsNull(@Param("anchor") AnchorEntity anchorEntity);
+
+ @Query(value
+ = "WITH RECURSIVE parent_search AS ("
+ + " SELECT id, 0 AS depth "
+ + " FROM fragment "
+ + " WHERE anchor_id = :anchorId AND xpath IN :xpaths "
+ + " UNION "
+ + " SELECT c.id, depth + 1 "
+ + " FROM fragment c INNER JOIN parent_search p ON c.parent_id = p.id"
+ + " WHERE depth <= (SELECT CASE WHEN :maxDepth = -1 THEN " + Integer.MAX_VALUE + " ELSE :maxDepth END) "
+ + ") "
+ + "SELECT f.id, anchor_id AS anchorId, xpath, f.parent_id AS parentId, CAST(attributes AS TEXT) AS attributes "
+ + "FROM fragment f INNER JOIN parent_search p ON f.id = p.id",
+ nativeQuery = true
+ )
+ List<FragmentExtract> findExtractsWithDescendants(@Param("anchorId") int anchorId,
+ @Param("xpaths") Collection<String> xpaths,
+ @Param("maxDepth") int maxDepth);
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryMultiPathQuery.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryMultiPathQuery.java deleted file mode 100644 index 9c34a459e9..0000000000 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryMultiPathQuery.java +++ /dev/null @@ -1,31 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.spi.repository; - -import java.util.Collection; -import java.util.List; -import org.onap.cps.spi.entities.FragmentEntity; - -public interface FragmentRepositoryMultiPathQuery { - - List<FragmentEntity> findByAnchorAndMultipleCpsPaths(Integer anchorId, Collection<String> cpsPathQuery); - -} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryMultiPathQueryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryMultiPathQueryImpl.java deleted file mode 100644 index 151fe97b34..0000000000 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryMultiPathQueryImpl.java +++ /dev/null @@ -1,59 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.spi.repository; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.transaction.Transactional; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.spi.entities.FragmentEntity; - -@Slf4j -@RequiredArgsConstructor -public class FragmentRepositoryMultiPathQueryImpl implements FragmentRepositoryMultiPathQuery { - - @PersistenceContext - private final EntityManager entityManager; - - private final TempTableCreator tempTableCreator; - - @Override - @Transactional - public List<FragmentEntity> findByAnchorAndMultipleCpsPaths(final Integer anchorId, - final Collection<String> cpsPathQueryList) { - if (cpsPathQueryList.isEmpty()) { - return Collections.emptyList(); - } - final String tempTableName = tempTableCreator.createTemporaryTable( - "xpathTemporaryTable", cpsPathQueryList, "xpath"); - final String sql = String.format( - "SELECT * FROM FRAGMENT WHERE anchor_id = %d AND xpath IN (select xpath FROM %s);", - anchorId, tempTableName); - final List<FragmentEntity> fragmentEntities = entityManager.createNativeQuery(sql, FragmentEntity.class) - .getResultList(); - log.debug("Fetched {} fragment entities by anchor and cps path.", fragmentEntities.size()); - return fragmentEntities; - } -} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/TempTableCreator.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/TempTableCreator.java index 338b0b1c64..139a8b3063 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/TempTableCreator.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/TempTableCreator.java @@ -20,10 +20,8 @@ package org.onap.cps.spi.repository; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -60,29 +58,12 @@ public class TempTableCreator { final StringBuilder sqlStringBuilder = new StringBuilder("CREATE TEMPORARY TABLE "); sqlStringBuilder.append(tempTableName); defineColumns(sqlStringBuilder, columnNames); + sqlStringBuilder.append(" ON COMMIT DROP;"); insertData(sqlStringBuilder, tempTableName, columnNames, sqlData); entityManager.createNativeQuery(sqlStringBuilder.toString()).executeUpdate(); return tempTableName; } - /** - * Create a uniquely named temporary table with a single column. - * - * @param prefix prefix for the table name (so you can recognize it) - * @param sqlData data to insert (strings only); each entry is a single row of data - * @param columnName column name - * @return a unique temporary table name with given prefix - */ - public String createTemporaryTable(final String prefix, - final Collection<String> sqlData, - final String columnName) { - final Collection<List<String>> tableData = new ArrayList<>(sqlData.size()); - for (final String entry : sqlData) { - tableData.add(Collections.singletonList(entry)); - } - return createTemporaryTable(prefix, tableData, columnName); - } - private static void defineColumns(final StringBuilder sqlStringBuilder, final String[] columnNames) { sqlStringBuilder.append('('); final Iterator<String> it = Arrays.stream(columnNames).iterator(); @@ -95,7 +76,7 @@ public class TempTableCreator { sqlStringBuilder.append(","); } } - sqlStringBuilder.append(");"); + sqlStringBuilder.append(")"); } private static void insertData(final StringBuilder sqlStringBuilder, diff --git a/cps-ri/src/main/resources/changelog/changelog-master.yaml b/cps-ri/src/main/resources/changelog/changelog-master.yaml index cb5392ba57..43a54caf64 100644 --- a/cps-ri/src/main/resources/changelog/changelog-master.yaml +++ b/cps-ri/src/main/resources/changelog/changelog-master.yaml @@ -1,6 +1,6 @@ # ============LICENSE_START======================================================= # Copyright (c) 2021 Bell Canada. -# Modifications Copyright (C) 2022 Nordix Foundation. +# Modifications Copyright (C) 2022-2023 Nordix Foundation. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -47,4 +47,6 @@ databaseChangeLog: - include: file: changelog/db/changes/15-rename-column-yang-resource-table.yaml - include: - file: changelog/db/changes/16-insert-cm-handle-state.yaml
\ No newline at end of file + file: changelog/db/changes/16-insert-cm-handle-state.yaml + - include: + file: changelog/db/changes/17-add-index-to-schema-set-yang-resources.yaml diff --git a/cps-ri/src/main/resources/changelog/db/changes/17-add-index-to-schema-set-yang-resources.yaml b/cps-ri/src/main/resources/changelog/db/changes/17-add-index-to-schema-set-yang-resources.yaml new file mode 100644 index 0000000000..bc16725109 --- /dev/null +++ b/cps-ri/src/main/resources/changelog/db/changes/17-add-index-to-schema-set-yang-resources.yaml @@ -0,0 +1,15 @@ +databaseChangeLog: + - changeSet: + author: cps + id: 17 + changes: + - createIndex: + columns: + - column: + name: schema_set_id + indexName: FKI_SCHEMA_SET_YANG_RESOURCES_SCHEMA_SET_ID_FK + tableName: schema_set_yang_resources + rollback: + - dropIndex: + indexName: FKI_SCHEMA_SET_YANG_RESOURCES_SCHEMA_SET_ID_FK + tableName: schema_set_yang_resources diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy index 28916b1c4a..e60afa78df 100755 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy @@ -25,7 +25,6 @@ package org.onap.cps.spi.impl import com.fasterxml.jackson.databind.ObjectMapper import com.google.common.collect.ImmutableSet -import org.onap.cps.cpspath.parser.PathParsingException import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.entities.FragmentEntity import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch @@ -71,13 +70,6 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { def static deleteTestChildXpath = "${deleteTestParentXPath}/child-with-slash[@key='a/b']" def static deleteTestGrandChildXPath = "${deleteTestChildXpath}/grandChild" - def expectedLeavesByXpathMap = [ - '/parent-207' : ['parent-leaf': 'parent-leaf value'], - '/parent-207/child-001' : ['first-child-leaf': 'first-child-leaf value'], - '/parent-207/child-002' : ['second-child-leaf': 'second-child-leaf value'], - '/parent-207/child-002/grand-child': ['grand-child-leaf': 'grand-child-leaf value'] - ] - @Sql([CLEAR_DATA, SET_DATA]) def 'Get all datanodes with descendants .'() { when: 'data nodes are retrieved by their xpath' @@ -188,7 +180,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { and: 'the (grand)child node of the new list entry is also present' def dataspaceEntity = dataspaceRepository.getByName(DATASPACE_NAME) def anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, ANCHOR_NAME3) - def grandChildFragmentEntity = fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, grandChild.xpath) + def grandChildFragmentEntity = fragmentRepository.findByAnchorAndXpath(anchorEntity, grandChild.xpath) assert grandChildFragmentEntity.isPresent() } @@ -213,7 +205,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { and: 'the new entity is inserted correctly' def dataspaceEntity = dataspaceRepository.getByName(DATASPACE_NAME) def anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT) - fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, '/parent-200/child-new2').isPresent() + fragmentRepository.findByAnchorAndXpath(anchorEntity, '/parent-200/child-new2').isPresent() } @Sql([CLEAR_DATA, SET_DATA]) @@ -253,8 +245,8 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { when: 'trying to execute a query with a syntax (parsing) error' objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, 'invalid-cps-path/child' , OMIT_DESCENDANTS) then: 'exception is thrown' - def exceptionThrown = thrown(PathParsingException) - assert exceptionThrown.getMessage().contains('failed to parse at line 1 due to extraneous input \'invalid-cps-path\' expecting \'/\'') + def exceptionThrown = thrown(CpsPathException) + assert exceptionThrown.getDetails() == "failed to parse at line 1 due to extraneous input 'invalid-cps-path' expecting '/'" } @Sql([CLEAR_DATA, SET_DATA]) @@ -288,7 +280,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { where: 'the following data is used' scenario | dataspaceName | anchorName | xpath || expectedException 'non existing xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | '/NO-XPATH' || DataNodeNotFoundException - 'invalid Xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | 'INVALID XPATH' || PathParsingException + 'invalid Xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | 'INVALID XPATH' || CpsPathException } @Sql([CLEAR_DATA, SET_DATA]) @@ -315,7 +307,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { 'root xpath' | ["/"] || 7 'empty (root) xpath' | [""] || 7 'root and top-level xpaths' | ["/", "/parent-200", "/parent-201"] || 7 - 'root and child xpaths' | ["/", "/parent-200/child-201"] || 8 + 'root and child xpaths' | ["/", "/parent-200/child-201"] || 7 } @Sql([CLEAR_DATA, SET_DATA]) @@ -667,35 +659,35 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { then: 'a #expectedException is thrown' thrown(expectedException) where: 'the following parameters were used' - scenario | datanodeXpath | expectedException - 'valid data node, non existent child node' | '/parent-203/child-non-existent' | DataNodeNotFoundException - 'invalid list element' | '/parent-206/child-206/grand-child-206@key="A"]' | PathParsingException + scenario | datanodeXpath | expectedException + 'valid data node, non existent child node' | '/parent-203/child-non-existent' | DataNodeNotFoundException + 'invalid list element' | '/parent-206/child-206/grand-child-206@key="A"]' | CpsPathException } @Sql([CLEAR_DATA, SET_DATA]) def 'Delete data node for an anchor.'() { given: 'a data-node exists for an anchor' - assert fragmentsExistInDB(1001, 3003) + assert fragmentsExistInDB(3003) when: 'data nodes are deleted ' objectUnderTest.deleteDataNodes(DATASPACE_NAME, ANCHOR_NAME3) then: 'all data-nodes are deleted successfully' - assert !fragmentsExistInDB(1001, 3003) + assert !fragmentsExistInDB(3003) } @Sql([CLEAR_DATA, SET_DATA]) def 'Delete data node for multiple anchors.'() { given: 'a data-node exists for an anchor' - assert fragmentsExistInDB(1001, 3001) - assert fragmentsExistInDB(1001, 3003) + assert fragmentsExistInDB(3001) + assert fragmentsExistInDB(3003) when: 'data nodes are deleted ' objectUnderTest.deleteDataNodes(DATASPACE_NAME, ['ANCHOR-001', 'ANCHOR-003']) then: 'all data-nodes are deleted successfully' - assert !fragmentsExistInDB(1001, 3001) - assert !fragmentsExistInDB(1001, 3003) + assert !fragmentsExistInDB(3001) + assert !fragmentsExistInDB(3003) } - def fragmentsExistInDB(dataSpaceId, anchorId) { - !fragmentRepository.findRootsByDataspaceAndAnchor(dataSpaceId, anchorId).isEmpty() + def fragmentsExistInDB(anchorId) { + fragmentRepository.existsByAnchorId(anchorId) } static Collection<DataNode> toDataNodes(xpaths) { @@ -711,19 +703,6 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { return jsonObjectMapper.convertJsonString(fragmentEntity.attributes, Map<String, Object>.class) } - def static assertLeavesMaps(actualLeavesMap, expectedLeavesMap) { - expectedLeavesMap.forEach((key, value) -> { - def actualValue = actualLeavesMap[key] - if (value instanceof Collection<?> && actualValue instanceof Collection<?>) { - assert value.size() == actualValue.size() - assert value.containsAll(actualValue) - } else { - assert value == actualValue - } - }) - return true - } - def static treeToFlatMapByXpath(Map<String, DataNode> flatMap, DataNode dataNodeTree) { flatMap.put(dataNodeTree.xpath, dataNodeTree) dataNodeTree.childDataNodes @@ -757,10 +736,9 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { def getFragmentByXpath(dataspaceName, anchorName, xpath) { def dataspace = dataspaceRepository.getByName(dataspaceName) def anchor = anchorRepository.getByDataspaceAndName(dataspace, anchorName) - return fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspace, anchor, xpath).orElseThrow() + return fragmentRepository.findByAnchorAndXpath(anchor, xpath).orElseThrow() } - def createChildListAllHavingAttributeValue(parentXpath, tag, Collection keys, boolean addGrandChild) { def listElementAsDataNodes = keysToXpaths(parentXpath, keys).collect { new DataNodeBuilder() diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy index ba42a083ea..3d7003d2a7 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy @@ -24,7 +24,9 @@ import com.fasterxml.jackson.databind.ObjectMapper import org.hibernate.StaleStateException import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.entities.AnchorEntity +import org.onap.cps.spi.entities.DataspaceEntity import org.onap.cps.spi.entities.FragmentEntity +import org.onap.cps.spi.entities.FragmentExtract import org.onap.cps.spi.exceptions.ConcurrencyException import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.DataNode @@ -48,6 +50,12 @@ class CpsDataPersistenceServiceSpec extends Specification { def objectUnderTest = Spy(new CpsDataPersistenceServiceImpl(mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper, mockSessionManager)) + def anchorEntity = new AnchorEntity(id: 123, dataspace: new DataspaceEntity(id: 1)) + + def setup() { + mockAnchorRepository.getByDataspaceAndName(_, _) >> anchorEntity + } + def 'Storing data nodes individually when batch operation fails'(){ given: 'two data nodes and supporting repository mock behavior' def dataNode1 = createDataNodeAndMockRepositoryMethodSupportingIt('xpath1','OK') @@ -57,7 +65,7 @@ class CpsDataPersistenceServiceSpec extends Specification { when: 'trying to store data nodes' objectUnderTest.storeDataNodes('dataSpaceName', 'anchorName', [dataNode1, dataNode2]) then: 'the two data nodes are saved individually' - 2 * mockFragmentRepository.save(_); + 2 * mockFragmentRepository.save(_) } def 'Store single data node.'() { @@ -71,7 +79,7 @@ class CpsDataPersistenceServiceSpec extends Specification { def 'Handling of StaleStateException (caused by concurrent updates) during update data node and descendants.'() { given: 'the fragment repository returns a fragment entity' - mockFragmentRepository.getByDataspaceAndAnchorAndXpath(*_) >> { + mockFragmentRepository.getByAnchorAndXpath(*_) >> { def fragmentEntity = new FragmentEntity() fragmentEntity.setChildFragments([new FragmentEntity()] as Set<FragmentEntity>) return fragmentEntity @@ -93,8 +101,6 @@ class CpsDataPersistenceServiceSpec extends Specification { '/node1': 'OK', '/node2': 'EXCEPTION', '/node3': 'EXCEPTION']) - and: 'db contains an anchor' - mockAnchorRepository.getByDataspaceAndName(*_) >> new AnchorEntity(id:123) and: 'the batch update will therefore also fail' mockFragmentRepository.saveAll(*_) >> { throw new StaleStateException("concurrent updates") } when: 'attempt batch update data nodes' @@ -144,13 +150,11 @@ class CpsDataPersistenceServiceSpec extends Specification { } def 'Retrieving multiple data nodes.'() { - given: 'db contains an anchor' - def anchorEntity = new AnchorEntity(id:123) - mockAnchorRepository.getByDataspaceAndName(*_) >> anchorEntity - and: 'fragment repository returns a collection of fragments' - def fragmentEntity1 = new FragmentEntity(xpath: '/xpath1', childFragments: []) - def fragmentEntity2 = new FragmentEntity(xpath: '/xpath2', childFragments: []) - mockFragmentRepository.findByAnchorAndMultipleCpsPaths(123, ['/xpath1', '/xpath2'] as Set<String>) >> [fragmentEntity1, fragmentEntity2] + given: 'fragment repository returns a collection of fragments' + mockFragmentRepository.findExtractsWithDescendants(123, ['/xpath1', '/xpath2'] as Set, _) >> [ + mockFragmentExtract(1, null, 123, '/xpath1', null), + mockFragmentExtract(2, null, 123, '/xpath2', null) + ] when: 'getting data nodes for 2 xpaths' def result = objectUnderTest.getDataNodesForMultipleXpaths('some-dataspace', 'some-anchor', ['/xpath1', '/xpath2'], FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) then: '2 data nodes are returned' @@ -182,7 +186,7 @@ class CpsDataPersistenceServiceSpec extends Specification { def 'update data node leaves: #scenario'(){ given: 'A node exists for the given xpath' - mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, '/some/xpath') >> new FragmentEntity(xpath: '/some/xpath', attributes: existingAttributes) + mockFragmentRepository.getByAnchorAndXpath(_, '/some/xpath') >> new FragmentEntity(xpath: '/some/xpath', attributes: existingAttributes) when: 'the node leaves are updated' objectUnderTest.updateDataLeaves('some-dataspace', 'some-anchor', '/some/xpath', newAttributes as Map<String, Serializable>) then: 'the fragment entity saved has the original and new attributes' @@ -200,10 +204,11 @@ class CpsDataPersistenceServiceSpec extends Specification { } def 'update data node and descendants: #scenario'(){ - given: 'mocked responses' - mockAnchorRepository.getByDataspaceAndName(_, _) >> new AnchorEntity(id:123) - mockFragmentRepository.findByAnchorAndMultipleCpsPaths(_, [] as Set) >> [] - mockFragmentRepository.findByAnchorAndMultipleCpsPaths(_, ['/test/xpath'] as Set) >> [new FragmentEntity(xpath: '/test/xpath', childFragments: [])] + given: 'the fragment repository returns fragment entities related to the xpath inputs' + mockFragmentRepository.findExtractsWithDescendants(_, [] as Set, _) >> [] + mockFragmentRepository.findExtractsWithDescendants(_, ['/test/xpath'] as Set, _) >> [ + mockFragmentExtract(1, null, 123, '/test/xpath', null) + ] when: 'replace data node tree' objectUnderTest.updateDataNodesAndDescendants('dataspaceName', 'anchorName', dataNodes) then: 'call fragment repository save all method' @@ -216,11 +221,10 @@ class CpsDataPersistenceServiceSpec extends Specification { def 'update data nodes and descendants'() { given: 'the fragment repository returns fragment entities related to the xpath inputs' - mockFragmentRepository.findByAnchorAndMultipleCpsPaths(_, ['/test/xpath1', '/test/xpath2'] as Set) >> [ - new FragmentEntity(xpath: '/test/xpath1', childFragments: []), - new FragmentEntity(xpath: '/test/xpath2', childFragments: [])] - and: 'db contains an anchor' - mockAnchorRepository.getByDataspaceAndName(*_) >> new AnchorEntity(id:123) + mockFragmentRepository.findExtractsWithDescendants(123, ['/test/xpath1', '/test/xpath2'] as Set, _) >> [ + mockFragmentExtract(1, null, 123, '/test/xpath1', null), + mockFragmentExtract(2, null, 123, '/test/xpath2', null) + ] and: 'some data nodes with descendants' def dataNode1 = new DataNode(xpath: '/test/xpath1', leaves: ['id': 'testId1'], childDataNodes: [new DataNode(xpath: '/test/xpath1/child', leaves: ['id': 'childTestId1'])]) def dataNode2 = new DataNode(xpath: '/test/xpath2', leaves: ['id': 'testId2'], childDataNodes: [new DataNode(xpath: '/test/xpath2/child', leaves: ['id': 'childTestId2'])]) @@ -239,7 +243,7 @@ class CpsDataPersistenceServiceSpec extends Specification { def createDataNodeAndMockRepositoryMethodSupportingIt(xpath, scenario) { def dataNode = new DataNodeBuilder().withXpath(xpath).build() def fragmentEntity = new FragmentEntity(xpath: xpath, childFragments: []) - mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, xpath) >> fragmentEntity + mockFragmentRepository.getByAnchorAndXpath(_, xpath) >> fragmentEntity if ('EXCEPTION' == scenario) { mockFragmentRepository.save(fragmentEntity) >> { throw new StaleStateException("concurrent updates") } } @@ -248,28 +252,39 @@ class CpsDataPersistenceServiceSpec extends Specification { def createDataNodesAndMockRepositoryMethodSupportingThem(Map<String, String> xpathToScenarioMap) { def dataNodes = [] - def fragmentEntities = [] + def fragmentExtracts = [] + def fragmentId = 1 xpathToScenarioMap.each { def xpath = it.key def scenario = it.value def dataNode = new DataNodeBuilder().withXpath(xpath).build() dataNodes.add(dataNode) - def fragmentEntity = new FragmentEntity(xpath: xpath, childFragments: []) - fragmentEntities.add(fragmentEntity) + def fragmentExtract = mockFragmentExtract(fragmentId, null, null, xpath, null) + fragmentExtracts.add(fragmentExtract) + def fragmentEntity = new FragmentEntity(id: fragmentId, xpath: xpath, childFragments: []) mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, xpath) >> fragmentEntity if ('EXCEPTION' == scenario) { mockFragmentRepository.save(fragmentEntity) >> { throw new StaleStateException("concurrent updates") } } + fragmentId++ } - mockFragmentRepository.findByAnchorAndMultipleCpsPaths(_, xpathToScenarioMap.keySet()) >> fragmentEntities + mockFragmentRepository.findExtractsWithDescendants(_, xpathToScenarioMap.keySet(), _) >> fragmentExtracts return dataNodes } def mockFragmentWithJson(json) { - def anchorEntity = new AnchorEntity(id:123) - mockAnchorRepository.getByDataspaceAndName(*_) >> anchorEntity - def fragmentEntity = new FragmentEntity(xpath: '/parent-01', childFragments: [], attributes: json) - mockFragmentRepository.findByAnchorAndMultipleCpsPaths(123, ['/parent-01'] as Set<String>) >> [fragmentEntity] + def fragmentExtract = mockFragmentExtract(456, null, null, '/parent-01', json) + mockFragmentRepository.findExtractsWithDescendants(123, ['/parent-01'] as Set, _) >> [fragmentExtract] + } + + def mockFragmentExtract(id, parentId, anchorId, xpath, attributes) { + def fragmentExtract = Mock(FragmentExtract) + fragmentExtract.getId() >> id + fragmentExtract.getParentId() >> parentId + fragmentExtract.getAnchorId() >> anchorId + fragmentExtract.getXpath() >> xpath + fragmentExtract.getAttributes() >> attributes + return fragmentExtract } } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy index eb138b98be..42698a65d6 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy @@ -50,8 +50,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase } stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Delete 5 children', 300, deleteDurationInMillis) + then: 'delete duration is under 150 milliseconds' + recordAndAssertPerformance('Delete 5 children', 150, deleteDurationInMillis) } def 'Batch delete 100 children with grandchildren'() { @@ -77,8 +77,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase } stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Delete 50 grandchildren', 300, deleteDurationInMillis) + then: 'delete duration is under 600 milliseconds' + recordAndAssertPerformance('Delete 50 grandchildren', 600, deleteDurationInMillis) } def 'Batch delete 500 grandchildren (that have no descendants)'() { @@ -94,8 +94,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase objectUnderTest.deleteDataNodes(PERF_DATASPACE, PERF_ANCHOR, xpathsToDelete) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 75 milliseconds' - recordAndAssertPerformance('Batch delete 500 grandchildren', 75, deleteDurationInMillis) + then: 'delete duration is under 100 milliseconds' + recordAndAssertPerformance('Batch delete 500 grandchildren', 100, deleteDurationInMillis) } @Sql([CLEAR_DATA, PERF_TEST_DATA]) @@ -105,8 +105,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase createLineage(objectUnderTest, 150, 50, true) stopWatch.stop() def setupDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'setup duration is under 5 seconds' - recordAndAssertPerformance('Setup lists', 5_000, setupDurationInMillis) + then: 'setup duration is under 6 seconds' + recordAndAssertPerformance('Setup lists', 6_000, setupDurationInMillis) } def 'Delete 5 whole lists'() { @@ -118,8 +118,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase } stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 1300 milliseconds' - recordAndAssertPerformance('Delete 5 whole lists', 1300, deleteDurationInMillis) + then: 'delete duration is under 130 milliseconds' + recordAndAssertPerformance('Delete 5 whole lists', 130, deleteDurationInMillis) } def 'Batch delete 100 whole lists'() { @@ -132,8 +132,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase objectUnderTest.deleteDataNodes(PERF_DATASPACE, PERF_ANCHOR, xpathsToDelete) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 500 milliseconds' - recordAndAssertPerformance('Batch delete 100 whole lists', 500, deleteDurationInMillis) + then: 'delete duration is under 600 milliseconds' + recordAndAssertPerformance('Batch delete 100 whole lists', 600, deleteDurationInMillis) } def 'Delete 10 list elements'() { @@ -145,8 +145,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase } stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 600 milliseconds' - recordAndAssertPerformance('Delete 10 lists elements', 600, deleteDurationInMillis) + then: 'delete duration is under 180 milliseconds' + recordAndAssertPerformance('Delete 10 lists elements', 180, deleteDurationInMillis) } def 'Batch delete 500 list elements'() { @@ -162,8 +162,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase objectUnderTest.deleteDataNodes(PERF_DATASPACE, PERF_ANCHOR, xpathsToDelete) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 60 milliseconds' - recordAndAssertPerformance('Batch delete 500 lists elements', 60, deleteDurationInMillis) + then: 'delete duration is under 70 milliseconds' + recordAndAssertPerformance('Batch delete 500 lists elements', 70, deleteDurationInMillis) } @Sql([CLEAR_DATA, PERF_TEST_DATA]) @@ -176,8 +176,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase objectUnderTest.deleteDataNode(PERF_DATASPACE, PERF_ANCHOR, PERF_TEST_PARENT) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Delete one large node', 300, deleteDurationInMillis) + then: 'delete duration is under 220 milliseconds' + recordAndAssertPerformance('Delete one large node', 220, deleteDurationInMillis) } @Sql([CLEAR_DATA, PERF_TEST_DATA]) @@ -190,8 +190,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase objectUnderTest.deleteDataNodes(PERF_DATASPACE, PERF_ANCHOR, [PERF_TEST_PARENT]) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 200 milliseconds' - recordAndAssertPerformance('Batch delete one large node', 200, deleteDurationInMillis) + then: 'delete duration is under 220 milliseconds' + recordAndAssertPerformance('Batch delete one large node', 220, deleteDurationInMillis) } @Sql([CLEAR_DATA, PERF_TEST_DATA]) @@ -204,8 +204,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase objectUnderTest.deleteDataNode(PERF_DATASPACE, PERF_ANCHOR, '/') stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 250 milliseconds' - recordAndAssertPerformance('Delete root node', 250, deleteDurationInMillis) + then: 'delete duration is under 300 milliseconds' + recordAndAssertPerformance('Delete root node', 300, deleteDurationInMillis) } @Sql([CLEAR_DATA, PERF_TEST_DATA]) @@ -218,8 +218,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase objectUnderTest.deleteDataNodes(PERF_DATASPACE, PERF_ANCHOR) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 250 milliseconds' - recordAndAssertPerformance('Delete data nodes for anchor', 250, deleteDurationInMillis) + then: 'delete duration is under 300 milliseconds' + recordAndAssertPerformance('Delete data nodes for anchor', 300, deleteDurationInMillis) } @Sql([CLEAR_DATA, PERF_TEST_DATA]) @@ -232,8 +232,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase objectUnderTest.deleteDataNodes(PERF_DATASPACE, [PERF_ANCHOR]) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 250 milliseconds' - recordAndAssertPerformance('Delete data nodes for anchors', 250, deleteDurationInMillis) + then: 'delete duration is under 300 milliseconds' + recordAndAssertPerformance('Delete data nodes for anchors', 300, deleteDurationInMillis) } } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy index 3562419c05..98ff211a60 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy @@ -72,8 +72,8 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistencePerfSpecBase { assert countDataNodes(result[0]) == TOTAL_NUMBER_OF_NODES where: 'the following xPaths are used' scenario | xpath || allowedDuration - 'parent' | PERF_TEST_PARENT || 3500 - 'root' | '' || 500 + 'parent' | PERF_TEST_PARENT || 500 + 'root' | '/' || 500 } def 'Query parent data node with many descendants by cps-path'() { @@ -82,8 +82,8 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistencePerfSpecBase { def result = objectUnderTest.queryDataNodes(PERF_DATASPACE, PERF_ANCHOR, '//perf-parent-1' , INCLUDE_ALL_DESCENDANTS) stopWatch.stop() def readDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'read duration is under 500 milliseconds' - recordAndAssertPerformance('Query with many descendants', 500, readDurationInMillis) + then: 'read duration is under 350 milliseconds' + recordAndAssertPerformance('Query with many descendants', 350, readDurationInMillis) and: 'data node is returned with all the descendants populated' assert countDataNodes(result) == TOTAL_NUMBER_OF_NODES } @@ -97,8 +97,8 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistencePerfSpecBase { def readDurationInMillis = stopWatch.getTotalTimeMillis() then: 'the returned number of entities equal to the number of children * number of grandchildren' assert result.size() == xpathsToAllGrandChildren.size() - and: 'it took less then 3000ms' - recordAndAssertPerformance('Find multiple xpaths', 3000, readDurationInMillis) + and: 'it took less then 1000ms' + recordAndAssertPerformance('Find multiple xpaths', 1000, readDurationInMillis) } def 'Query many descendants by cps-path with #scenario'() { @@ -131,8 +131,8 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistencePerfSpecBase { objectUnderTest.updateDataNodesAndDescendants(PERF_DATASPACE, PERF_ANCHOR, dataNodes) stopWatch.stop() def updateDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'update duration is under 600 milliseconds' - recordAndAssertPerformance('Update data nodes with descendants', 600, updateDurationInMillis) + then: 'update duration is under 500 milliseconds' + recordAndAssertPerformance('Update data nodes with descendants', 500, updateDurationInMillis) } def 'Update data nodes without descendants'() { @@ -152,7 +152,7 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistencePerfSpecBase { objectUnderTest.updateDataNodesAndDescendants(PERF_DATASPACE, PERF_ANCHOR, dataNodes) stopWatch.stop() def updateDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'update duration is under 600 milliseconds' - recordAndAssertPerformance('Update data nodes without descendants', 600, updateDurationInMillis) + then: 'update duration is under 900 milliseconds' + recordAndAssertPerformance('Update data nodes without descendants', 900, updateDurationInMillis) } } |