2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2023 Nordix Foundation
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the 'License');
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an 'AS IS' BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.integration.performance.ncmp
23 import org.onap.cps.api.CpsQueryService
24 import org.onap.cps.integration.performance.base.NcmpPerfTestBase
25 import org.onap.cps.spi.model.DataNode
27 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
29 class CmDataSubscribersPerfTest extends NcmpPerfTestBase {
31 def datastore1cmHandlePlaceHolder = '{"datastores":{"datastore":[{"name":"ds-1","cm-handles":{"cm-handle":[]}}]}}'
32 def xPathForDataStore1CmHandles = '/datastores/datastore[@name="ds-1"]/cm-handles'
34 CpsQueryService objectUnderTest
36 def setup() { objectUnderTest = cpsQueryService }
38 def totalNumberOfEntries = numberOfFiltersPerCmHandle * numberOfCmHandlesPerCmDataSubscription
40 def random = new Random()
42 def 'Find many subscribers in large dataset.'() {
43 when: 'all filters are queried'
45 def cpsPath = '//filter'
46 def result = objectUnderTest.queryDataNodes(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, cpsPath, INCLUDE_ALL_DESCENDANTS)
47 then: 'got all filter entries'
48 result.size() == totalNumberOfEntries
49 then: 'find a random subscriptions by iteration (worst case: whole subscription matches previous entries)'
50 def matches = querySubscriptionsByIteration(result, -1)
52 matches.size() == numberOfFiltersPerCmHandle * numberOfCmHandlesPerCmDataSubscription
53 and: 'query all subscribers within 1 second'
54 def durationInMillis = stopWatch.getTotalTimeMillis()
55 recordAndAssertPerformance("Query all subscribers", 1_000, durationInMillis)
58 def 'Worst case new subscription (200x10 new entries).'() {
59 given: 'a new subscription with non-matching data'
60 def subscribers = createLeafList('subscribers',1, subscriberIdPrefix)
61 def filters = '"filters":' + createJsonArray('filter',numberOfFiltersPerCmHandle,'xpath','other_' + xpathPrefix,subscribers)
62 def cmHandles = createJsonArray('cm-handle',numberOfCmHandlesPerCmDataSubscription,'id','other' + cmHandlePrefix, filters)
63 when: 'Insert a new subscription'
65 cpsDataService.saveData(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, xPathForDataStore1CmHandles, cmHandles, now)
67 def durationInMillis = stopWatch.getTotalTimeMillis()
68 then: 'insert new subscription with 1 second'
69 recordAndAssertPerformance("Insert new subscription", 1_000, durationInMillis)
72 def 'Worst case subscription update (200x10 matching entries).'() {
73 given: 'all filters are queried'
74 def cpsPath = '//filter'
75 def result = objectUnderTest.queryDataNodes(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, cpsPath, INCLUDE_ALL_DESCENDANTS)
76 and: 'find all entries for an existing subscriptions'
77 def matches = querySubscriptionsByIteration(result, 1)
78 when: 'Update all subscriptions found'
80 /* the production code version of this should manipulate the original subscribersAsArray of course
81 but for the (performance) poc creating another array with one extra element suffices
84 matches.each { xpath, subscribersAsArray ->
85 def updatedSubscribers = createLeafList('subscribers', 1 + numberOfCmDataSubscribers, subscriberIdPrefix)
86 def filterEntry = '{"filter": {"xpath":"' + xpath + '", ' + updatedSubscribers + ' } }'
87 def parentPath = xpath.toString().substring(0, xpath.toString().indexOf('/filter[@xpath='))
88 jsonPerPath.put(parentPath, filterEntry)
90 cpsDataService.updateDataNodesAndDescendants(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, jsonPerPath, now)
92 def durationInMillis = stopWatch.getTotalTimeMillis()
93 then: 'Update matching subscription within 8 seconds'
94 //TODO Toine check with Daniel if this can be optimized quickly without really changing production code
95 // ie is there a better way of doing these 2,000 updates
96 recordAndAssertPerformance("Update matching subscription", 8_000, durationInMillis)
99 def querySubscriptionsByIteration(Collection<DataNode> allSubscriptionsAsDataNodes, targetSubscriptionSequenceNumber) {
101 allSubscriptionsAsDataNodes.each {
102 String[] subscribersAsArray = it.leaves.get('subscribers')
103 Set<String> subscribersAsSet = new HashSet<>(Arrays.asList(subscribersAsArray))
104 def targetSubscriptionId = subscriberIdPrefix + '-' + ( targetSubscriptionSequenceNumber > 0 ? targetSubscriptionSequenceNumber
105 : 1 + random.nextInt(numberOfCmDataSubscribers) )
106 if (subscribersAsSet.contains(targetSubscriptionId)) {
107 matches.put(it.xpath, subscribersAsArray)