Merge "CM Data Subscriptions PoC/Performance test fixes"
authorToine Siebelink <toine.siebelink@est.tech>
Tue, 26 Sep 2023 17:17:31 +0000 (17:17 +0000)
committerGerrit Code Review <gerrit@onap.org>
Tue, 26 Sep 2023 17:17:31 +0000 (17:17 +0000)
1  2 
integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmDataSubscriptionsPerfTest.groovy

@@@ -26,7 -26,7 +26,7 @@@ import org.onap.cps.spi.model.DataNod
  
  import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
  
 -class CmDataSubscribersPerfTest extends NcmpPerfTestBase {
 +class CmDataSubscriptionsPerfTest extends NcmpPerfTestBase {
  
      def datastore1cmHandlePlaceHolder = '{"datastores":{"datastore":[{"name":"ds-1","cm-handles":{"cm-handle":[]}}]}}'
      def xPathForDataStore1CmHandles = '/datastores/datastore[@name="ds-1"]/cm-handles'
              recordAndAssertPerformance("Query all subscribers", 1_000, durationInMillis)
      }
  
-     def 'Worst case new subscription (200x10 new entries).'() {
-         given: 'a new subscription with non-matching data'
-             def subscribers = createLeafList('subscribers',1, subscriberIdPrefix)
-             def filters = '"filters":' + createJsonArray('filter',numberOfFiltersPerCmHandle,'xpath','other_' + xpathPrefix,subscribers)
-             def cmHandles = createJsonArray('cm-handle',numberOfCmHandlesPerCmDataSubscription,'id','other' + cmHandlePrefix, filters)
-         when: 'Insert a new subscription'
-             stopWatch.start()
-             cpsDataService.saveData(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, xPathForDataStore1CmHandles, cmHandles, now)
-             stopWatch.stop()
-             def durationInMillis = stopWatch.getTotalTimeMillis()
-         then: 'insert new subscription with 1 second'
-             recordAndAssertPerformance("Insert new subscription", 1_000, durationInMillis)
-     }
      def 'Worst case subscription update (200x10 matching entries).'() {
          given: 'all filters are queried'
              def cpsPath = '//filter'
              def result = objectUnderTest.queryDataNodes(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, cpsPath, INCLUDE_ALL_DESCENDANTS)
+         and: 'there are the expected number of subscribers per subscription'
+             assert result.collect {it.leaves.subscribers.size()}.sum() == totalNumberOfEntries * numberOfCmDataSubscribers
          and: 'find all entries for an existing subscriptions'
              def matches = querySubscriptionsByIteration(result, 1)
-         when: 'Update all subscriptions found'
+         when: 'update all subscriptions found'
              stopWatch.start()
-             /* the production code version of this should manipulate the original subscribersAsArray of course
-                but for the (performance) poc creating another array with one extra element suffices
-              */
-             def jsonPerPath = [:]
-             matches.each { xpath, subscribersAsArray ->
+             HashMap<String, List<String>> filterEntriesPerPath = [:]
+             matches.each { dataNode, subscribersAsArray ->
                  def updatedSubscribers = createLeafList('subscribers', 1 + numberOfCmDataSubscribers, subscriberIdPrefix)
-                 def filterEntry = '{"filter": {"xpath":"' + xpath + '", ' + updatedSubscribers + ' } }'
-                 def parentPath = xpath.toString().substring(0, xpath.toString().indexOf('/filter[@xpath='))
-                 jsonPerPath.put(parentPath, filterEntry)
+                 def filterEntry = '{"xpath":"' + dataNode.leaves.xpath + '", ' + updatedSubscribers + ' }'
+                 def parentPath = dataNode.xpath.toString().substring(0, dataNode.xpath.toString().indexOf('/filter[@xpath='))
+                 filterEntriesPerPath.putIfAbsent(parentPath, new ArrayList<String>())
+                 filterEntriesPerPath.get(parentPath).add(filterEntry)
+             }
+             HashMap<String, String> jsonPerPath = [:]
+             filterEntriesPerPath.each { parentPath, filterEntries ->
+                 jsonPerPath.put(parentPath, '{"filter": [' + filterEntries.join(',') + ']}')
              }
-             cpsDataService.updateDataNodesAndDescendants(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, jsonPerPath, now)
+             // NOTE Below fails as updateDataNodesAndDescendants can't handle JSON lists!
+             // cpsDataService.updateDataNodesAndDescendants(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, jsonPerPath, now)
+             // So update for each CM-handle instead:
+             jsonPerPath.each { parentPath, json ->
+                 // Around 8.5 seconds for long strings, 4.8 with short strings
+                 // cpsDataService.updateDataNodeAndDescendants(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, parentPath, json, now)
+                 // Around 6.5 seconds for long strings, 3.3 seconds with short strings
+                 cpsDataService.updateNodeLeaves(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, parentPath, json, now)
+             }
              stopWatch.stop()
              def durationInMillis = stopWatch.getTotalTimeMillis()
-         then: 'Update matching subscription within 8 seconds'
-             //TODO Toine check with Daniel if this can be optimized quickly without really changing production code
-             // ie is there a better way of doing these 2,000 updates
+         then: 'a subscriber has been added to each filter entry'
+             def resultAfter = objectUnderTest.queryDataNodes(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, cpsPath, INCLUDE_ALL_DESCENDANTS)
+             assert resultAfter.collect {it.leaves.subscribers.size()}.sum() == totalNumberOfEntries * (1 + numberOfCmDataSubscribers)
+         and: 'update matching subscription within 8 seconds'
              recordAndAssertPerformance("Update matching subscription", 8_000, durationInMillis)
      }
  
+     def 'Worst case new subscription (200x10 new entries).'() {
+         given: 'a new subscription with non-matching data'
+             def subscribers = createLeafList('subscribers',1, subscriberIdPrefix)
+             def filters = '"filters":' + createJsonArray('filter',numberOfFiltersPerCmHandle,'xpath','other_' + xpathPrefix,subscribers)
+             def cmHandles = createJsonArray('cm-handle',numberOfCmHandlesPerCmDataSubscription,'id','other' + cmHandlePrefix, filters)
+         when: 'Insert a new subscription'
+             stopWatch.start()
+             cpsDataService.saveData(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, xPathForDataStore1CmHandles, cmHandles, now)
+             stopWatch.stop()
+             def durationInMillis = stopWatch.getTotalTimeMillis()
+         then: 'insert new subscription with 1 second'
+             recordAndAssertPerformance("Insert new subscription", 1_000, durationInMillis)
+     }
      def querySubscriptionsByIteration(Collection<DataNode> allSubscriptionsAsDataNodes, targetSubscriptionSequenceNumber) {
          def matches = [:]
          allSubscriptionsAsDataNodes.each {
              def targetSubscriptionId = subscriberIdPrefix + '-' + ( targetSubscriptionSequenceNumber > 0 ? targetSubscriptionSequenceNumber
                                                                                                       : 1 + random.nextInt(numberOfCmDataSubscribers) )
              if (subscribersAsSet.contains(targetSubscriptionId)) {
-                 matches.put(it.xpath, subscribersAsArray)
+                 matches.put(it, subscribersAsArray)
              }
          }
          return matches