Improve performance of moduleSetTag lookup 47/137347/2
authordanielhanrahan <daniel.hanrahan@est.tech>
Wed, 21 Feb 2024 16:37:19 +0000 (16:37 +0000)
committerdanielhanrahan <daniel.hanrahan@est.tech>
Thu, 22 Feb 2024 10:52:40 +0000 (10:52 +0000)
- Using absolute cps-path /dmi-registry/cm-handles is faster
  than descendant cps-path //cm-handles due to CPS-2087.
- Converting DataNodes to YangModelCmHandle allows checking if
  the handles are READY without needing to send additional DB
  queries via cmHandleQueries::cmHandleHasState.

Issue-ID: CPS-2027
Signed-off-by: danielhanrahan <daniel.hanrahan@est.tech>
Change-Id: Ied7e884b0f779f394854603a7f8b186d09eb5be8

cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncServiceSpec.groovy

index 333a6f2..dabfbbc 100644 (file)
@@ -31,7 +31,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
@@ -113,34 +112,34 @@ public class ModuleSyncService {
     private ImmutableTriple<String, Map<String, String>, Collection<ModuleReference>>
         getAllModuleReferencesAndNewYangResourcesByModuleSetTag(final YangModelCmHandle yangModelCmHandle,
                                                                 final boolean inUpgrade) {
-
         final String moduleSetTag = getModuleSetTag(yangModelCmHandle, inUpgrade);
         final Collection<ModuleReference> allModuleReferences;
-        Map<String, String> newYangResources = Collections.emptyMap();
-
-        final Optional<DataNode> optionalDataNode = getFirstReadyDataNodeByModuleSetTagProvidedInDb(moduleSetTag);
+        final Map<String, String> newYangResources;
 
-        if (optionalDataNode.isPresent()) {
-            log.info("Found other cm handle having same module set tag: {}", moduleSetTag);
-            final String otherAnchorWithSameModuleSetTag
-                    = YangDataConverter.extractCmHandleIdFromXpath(optionalDataNode.get().getXpath());
-            allModuleReferences = cpsModuleService.getYangResourcesModuleReferences(
-                    NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, otherAnchorWithSameModuleSetTag);
-        } else {
+        final YangModelCmHandle cmHandleWithSameModuleSetTag = getAnyReadyCmHandleByModuleSetTag(moduleSetTag);
+        if (cmHandleWithSameModuleSetTag == null) {
             allModuleReferences = dmiModelOperations.getModuleReferences(yangModelCmHandle);
             newYangResources = getNewModuleNameToContentMap(yangModelCmHandle, allModuleReferences);
+        } else {
+            log.info("Found other cm handle having same module set tag: {}", moduleSetTag);
+            allModuleReferences = cpsModuleService.getYangResourcesModuleReferences(
+                    NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleWithSameModuleSetTag.getId());
+            newYangResources = NO_NEW_MODULES;
         }
         return ImmutableTriple.of(moduleSetTag, newYangResources, allModuleReferences);
     }
 
-    private Optional<DataNode> getFirstReadyDataNodeByModuleSetTagProvidedInDb(final String moduleSetTag) {
-        final List<DataNode> dataNodes = StringUtils.isNotBlank(moduleSetTag) ? cmHandleQueries
-                .queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='" + moduleSetTag + "']",
-                        FetchDescendantsOption.OMIT_DESCENDANTS) : Collections.emptyList();
-        return dataNodes.stream().filter(dataNode -> {
-            final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(dataNode.getXpath());
-            return cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY);
-        }).findFirst();
+    private YangModelCmHandle getAnyReadyCmHandleByModuleSetTag(final String moduleSetTag) {
+        if (StringUtils.isBlank(moduleSetTag)) {
+            return null;
+        }
+        final String escapedModuleSetTag = moduleSetTag.replace("'", "''");
+        final List<DataNode> dataNodes = cmHandleQueries.queryNcmpRegistryByCpsPath(
+                NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@module-set-tag='" + escapedModuleSetTag + "']",
+                FetchDescendantsOption.DIRECT_CHILDREN_ONLY);
+        return dataNodes.stream().map(YangDataConverter::convertCmHandleToYangModel)
+                .filter(cmHandle -> cmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY)
+                .findFirst().orElse(null);
     }
 
     private void setCmHandleModuleSetTag(final YangModelCmHandle upgradedCmHandle, final String moduleSetTag) {
index bc1f9dc..6ab2d54 100644 (file)
@@ -26,7 +26,6 @@ import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPE
 import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE
 
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
-import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
@@ -55,7 +54,10 @@ class ModuleSyncServiceSpec extends Specification {
             mockCmHandleQueries, mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper)
 
     def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
-    def static cmHandleWithModuleSetTag = new DataNodeBuilder().withXpath("//cm-handles[@module-set-tag='tag-1'][@id='otherId']").withAnchor('otherId').build()
+    def static cmHandleWithModuleSetTag = new DataNodeBuilder()
+            .withXpath("/dmi-registry/cm-handles[@id='otherId']")
+            .withLeaves(['id': 'otherId', 'module-set-tag': 'tag-1'])
+            .withAnchor('otherId').build()
 
     def 'Sync model for a NEW cm handle using module set tags: #scenario.'() {
         given: 'a cm handle state to be synced'
@@ -102,7 +104,7 @@ class ModuleSyncServiceSpec extends Specification {
         and: 'CPS-Core returns list of existing module resources for TBD'
             mockCpsModuleService.getYangResourcesModuleReferences(*_) >> [ new ModuleReference('module1','1') ]
         and: 'system contains #existingCmHandlesWithSameTag.size() cm handles with same tag'
-            mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='tag-1']", FetchDescendantsOption.OMIT_DESCENDANTS) >> existingCmHandlesWithSameTag
+            mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag
         and: 'the other cm handle is a state ready'
             mockCmHandleQueries.cmHandleHasState('otherId', CmHandleState.READY) >> true
         when: 'module sync is triggered'
@@ -131,8 +133,8 @@ class ModuleSyncServiceSpec extends Specification {
             def moduleReferences = [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
             mockCpsModuleService.getYangResourcesModuleReferences(*_)>> moduleReferences
         and: 'a cm handle with the same moduleSetTag can be found in the registry'
-            mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='targetModuleSetTag']",
-                FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1', 'cm-handle-state': 'READY'])]
+            mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1'],
+                    childDataNodes: [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']/state', leaves: ['cm-handle-state': 'READY'])])]
         when: 'module upgrade is triggered'
             objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle)
         then: 'the upgrade is delegated to the module service (with the correct parameters)'