aboutsummaryrefslogtreecommitdiffstats
path: root/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmDataSubscriptionsPerfTest.groovy
blob: 7601d30a476b5550178f91938bd6a71bb45ad351 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
 *  ============LICENSE_START=======================================================
 *  Copyright (C) 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.integration.performance.ncmp

import org.onap.cps.api.CpsQueryService
import org.onap.cps.integration.performance.base.NcmpPerfTestBase
import org.onap.cps.spi.model.DataNode

import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS

class CmDataSubscribersPerfTest extends NcmpPerfTestBase {

    def datastore1cmHandlePlaceHolder = '{"datastores":{"datastore":[{"name":"ds-1","cm-handles":{"cm-handle":[]}}]}}'
    def xPathForDataStore1CmHandles = '/datastores/datastore[@name="ds-1"]/cm-handles'

    CpsQueryService objectUnderTest

    def setup() { objectUnderTest = cpsQueryService }

    def totalNumberOfEntries = numberOfFiltersPerCmHandle * numberOfCmHandlesPerCmDataSubscription

    def random = new Random()

    def 'Find many subscribers in large dataset.'() {
        when: 'all filters are queried'
            stopWatch.start()
            def cpsPath = '//filter'
            def result = objectUnderTest.queryDataNodes(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, cpsPath, INCLUDE_ALL_DESCENDANTS)
        then: 'got all filter entries'
            result.size() == totalNumberOfEntries
        then: 'find a random subscriptions by iteration (worst case: whole subscription matches previous entries)'
            def matches = querySubscriptionsByIteration(result, -1)
            stopWatch.stop()
            matches.size() == numberOfFiltersPerCmHandle * numberOfCmHandlesPerCmDataSubscription
        and: 'query all subscribers within 1 second'
            def durationInMillis = stopWatch.getTotalTimeMillis()
            recordAndAssertPerformance("Query all subscribers", 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'
            stopWatch.start()
            HashMap<String, List<String>> filterEntriesPerPath = [:]
            matches.each { dataNode, subscribersAsArray ->
                def updatedSubscribers = createLeafList('subscribers', 1 + numberOfCmDataSubscribers, subscriberIdPrefix)
                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(',') + ']}')
            }

            // 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: '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 {
            String[] subscribersAsArray = it.leaves.get('subscribers')
            Set<String> subscribersAsSet = new HashSet<>(Arrays.asList(subscribersAsArray))
            def targetSubscriptionId = subscriberIdPrefix + '-' + ( targetSubscriptionSequenceNumber > 0 ? targetSubscriptionSequenceNumber
                                                                                                     : 1 + random.nextInt(numberOfCmDataSubscribers) )
            if (subscribersAsSet.contains(targetSubscriptionId)) {
                matches.put(it, subscribersAsArray)
            }
        }
        return matches
    }

}