From 2e4fbdf79514ecf1c6a5387d70045c498e325d8a Mon Sep 17 00:00:00 2001 From: danielhanrahan Date: Tue, 28 Mar 2023 16:43:10 +0100 Subject: Performance tests for getDataNodes and queryDataNodes - Generate openroadm data from a single innerNode.json template - Double the number of openroadm device nodes (25 -> 50) in tests - Add new performance tests for getDataNodes and queryDataNodes to integration-test module, using openroadm and bookstore data - Remove old performance tests from cps-ri Issue-ID: CPS-1524 Signed-off-by: danielhanrahan Change-Id: Id9ec2a86d984d6c50c9ae6093e7a62729cb851da --- .../performance/base/CpsPerfTestBase.groovy | 15 ++- .../integration/performance/cps/GetPerfTest.groovy | 56 +++++++++-- .../performance/cps/QueryPerfTest.groovy | 106 +++++++++++++++++++++ .../performance/ncmp/CmHandleQueryPerfTest.groovy | 14 +-- 4 files changed, 168 insertions(+), 23 deletions(-) create mode 100644 integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy (limited to 'integration-test/src/test/groovy/org/onap') diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy index e75f1dce36..3b5f69c6e0 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy @@ -58,7 +58,7 @@ class CpsPerfTestBase extends PerfTestBase { addAnchorsWithData(1, CpsIntegrationSpecBase.BOOKSTORE_SCHEMA_SET, 'warmup', data) stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() - recordAndAssertPerformance('Creating warmup anchor with tiny data tree', 250, durationInMillis) + recordAndAssertPerformance('Creating warmup anchor with tiny data tree', 500, durationInMillis) } def createLargeBookstoresData() { @@ -79,7 +79,7 @@ class CpsPerfTestBase extends PerfTestBase { } def addOpenRoadData() { - def data = CpsIntegrationSpecBase.readResourceDataFile('openroadm/innerNode.json') + def data = generateOpenRoadData(50) stopWatch.start() addAnchorsWithData(5, PerfTestBase.LARGE_SCHEMA_SET, 'openroadm', data) stopWatch.stop() @@ -87,6 +87,13 @@ class CpsPerfTestBase extends PerfTestBase { recordAndAssertPerformance('Creating openroadm anchors with large data tree', 25_000, durationInMillis) } + def generateOpenRoadData(numberOfNodes) { + def innerNode = CpsIntegrationSpecBase.readResourceDataFile('openroadm/innerNode.json') + return '{ "openroadm-devices": { "openroadm-device": [' + + (1..numberOfNodes).collect { innerNode.replace('NODE_ID_HERE', it.toString()) }.join(',') + + ']}}' + } + def addAnchorsWithData(numberOfAnchors, schemaSetName, anchorNamePrefix, data) { (1..numberOfAnchors).each { cpsAdminService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, schemaSetName, anchorNamePrefix + it) @@ -101,8 +108,8 @@ class CpsPerfTestBase extends PerfTestBase { assert countDataNodesInTree(result) == 1 stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() - then: 'all data is read within 15 seconds (warm up not critical)' - recordAndAssertPerformance("Warming database", 15_000, durationInMillis) + then: 'all data is read within 25 seconds (warm up not critical)' + recordAndAssertPerformance("Warming database", 25_000, durationInMillis) } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy index 30e8bf23d4..4edc1d72ad 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy @@ -21,7 +21,11 @@ package org.onap.cps.integration.performance.cps import org.onap.cps.integration.performance.base.CpsPerfTestBase -import org.onap.cps.spi.FetchDescendantsOption +import org.springframework.dao.DataAccessResourceFailureException + +import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY +import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS class GetPerfTest extends CpsPerfTestBase { @@ -29,11 +33,40 @@ class GetPerfTest extends CpsPerfTestBase { def setup() { objectUnderTest = cpsDataService } - def 'Read complete data trees from multiple anchors with #scenario.'() { + def 'Read top-level node with #scenario.'() { + when: 'get data nodes from 1 anchor' + stopWatch.start() + def result = objectUnderTest.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchor, '/openroadm-devices', fetchDescendantsOption) + stopWatch.stop() + assert countDataNodesInTree(result) == expectedNumberOfDataNodes + def durationInMillis = stopWatch.getTotalTimeMillis() + then: 'all data is read within #durationLimit ms' + recordAndAssertPerformance("Read datatrees with ${scenario}", durationLimit, durationInMillis) + where: 'the following parameters are used' + scenario | fetchDescendantsOption | anchor || durationLimit | expectedNumberOfDataNodes + 'no descendants' | OMIT_DESCENDANTS | 'openroadm1' || 100 | 1 + 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 100 | 1 + 50 + 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 350 | 1 + 50 * 86 + } + + def 'Read data trees for multiple xpaths'() { + given: 'a collection of xpaths to get' + def xpaths = (1..50).collect { "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']" } + when: 'get data nodes from 1 anchor' + stopWatch.start() + def result = objectUnderTest.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm4', xpaths, INCLUDE_ALL_DESCENDANTS) + stopWatch.stop() + assert countDataNodesInTree(result) == 50 * 86 + def durationInMillis = stopWatch.getTotalTimeMillis() + then: 'all data is read within 350 ms' + recordAndAssertPerformance("Read datatrees for multiple xpaths", 350, durationInMillis) + } + + def 'Read complete data trees using #scenario.'() { when: 'get data nodes for 5 anchors' stopWatch.start() (1..5).each { - def result = objectUnderTest.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchorPrefix + it, xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchorPrefix + it, xpath, INCLUDE_ALL_DESCENDANTS) assert countDataNodesInTree(result) == expectedNumberOfDataNodes } stopWatch.stop() @@ -42,10 +75,19 @@ class GetPerfTest extends CpsPerfTestBase { recordAndAssertPerformance("Read datatrees using ${scenario}", durationLimit, durationInMillis) where: 'the following xpaths are used' scenario | anchorPrefix | xpath || durationLimit | expectedNumberOfDataNodes - 'bookstore root' | 'bookstore' | '/' || 130 | 78 - 'bookstore top element' | 'bookstore' | '/bookstore' || 130 | 78 - 'openroadm root' | 'openroadm' | '/' || 750 | 2151 - 'openroadm top element' | 'openroadm' | '/openroadm-devices' || 750 | 2151 + 'bookstore root' | 'bookstore' | '/' || 250 | 78 + 'bookstore top element' | 'bookstore' | '/bookstore' || 250 | 78 + 'openroadm root' | 'openroadm' | '/' || 1000 | 1 + 50 * 86 + 'openroadm top element' | 'openroadm' | '/openroadm-devices' || 1000 | 1 + 50 * 86 + } + + def 'Multiple get limit exceeded: 32,764 (~ 2^15) xpaths.'() { + given: 'more than 32,764 xpaths)' + def xpaths = (0..32_764).collect { "/size/of/this/path/does/not/matter/for/limit[@id='" + it + "']" } + when: 'single get is executed to get all the parent objects and their descendants' + cpsDataService.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'bookstore1', xpaths, INCLUDE_ALL_DESCENDANTS) + then: 'an exception is thrown' + thrown(DataAccessResourceFailureException.class) } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy new file mode 100644 index 0000000000..496842096f --- /dev/null +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy @@ -0,0 +1,106 @@ +/* + * ============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.cps + +import org.onap.cps.integration.performance.base.CpsPerfTestBase + +import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY +import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS + +class QueryPerfTest extends CpsPerfTestBase { + + def objectUnderTest + + def setup() { objectUnderTest = cpsQueryService } + + def 'Query complete data trees with #scenario.'() { + when: 'query data nodes (using a fresh anchor with identical data for each test)' + stopWatch.start() + def result = objectUnderTest.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchor, cpsPath, INCLUDE_ALL_DESCENDANTS) + stopWatch.stop() + def durationInMillis = stopWatch.getTotalTimeMillis() + then: 'the expected number of nodes is returned' + assert countDataNodesInTree(result) == expectedNumberOfDataNodes + and: 'all data is read within #durationLimit ms' + recordAndAssertPerformance("Query 1 anchor ${scenario}", durationLimit, durationInMillis) + where: 'the following parameters are used' + scenario | anchor | cpsPath || durationLimit | expectedNumberOfDataNodes + 'top element' | 'openroadm1' | '/openroadm-devices' || 250 | 50 * 86 + 1 + 'leaf condition' | 'openroadm2' | '//openroadm-device[@ne-state="inservice"]' || 650 | 50 * 86 + 'ancestors' | 'openroadm3' | '//openroadm-device/ancestor::openroadm-devices' || 250 | 50 * 86 + 1 + 'leaf condition + ancestors' | 'openroadm4' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 500 | 50 * 86 + 1 + } + + def 'Query complete data trees across all anchors with #scenario.'() { + when: 'query data nodes across all anchors' + stopWatch.start() + def result = objectUnderTest.queryDataNodesAcrossAnchors('cpsPerformanceDataspace', cpspath, INCLUDE_ALL_DESCENDANTS) + stopWatch.stop() + def durationInMillis = stopWatch.getTotalTimeMillis() + then: 'the expected number of nodes is returned' + assert countDataNodesInTree(result) == expectedNumberOfDataNodes + and: 'all data is read within #durationLimit ms' + recordAndAssertPerformance("Query across anchors ${scenario}", durationLimit, durationInMillis) + where: 'the following parameters are used' + scenario | cpspath || durationLimit | expectedNumberOfDataNodes + // FIXME Current implementation of queryDataNodesAcrossAnchors throws NullPointerException for next case. Uncomment after CPS-1582 is done. + // 'top element' | '/openroadm-devices' || 1 | 5 * (50 * 86 + 1) + 'leaf condition' | '//openroadm-device[@ne-state="inservice"]' || 2500 | 5 * (50 * 86) + 'ancestors' | '//openroadm-device/ancestor::openroadm-devices' || 12000 | 5 * (50 * 86 + 1) + 'leaf condition + ancestors' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 1000 | 5 * (50 * 86 + 1) + } + + def 'Query with leaf condition and #scenario.'() { + when: 'query data nodes (using a fresh anchor with identical data for each test)' + stopWatch.start() + def result = objectUnderTest.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchor, '//openroadm-device[@status="success"]', fetchDescendantsOption) + stopWatch.stop() + def durationInMillis = stopWatch.getTotalTimeMillis() + then: 'the expected number of nodes is returned' + assert countDataNodesInTree(result) == expectedNumberOfDataNodes + and: 'all data is read within #durationLimit ms' + recordAndAssertPerformance("Query with ${scenario}", durationLimit, durationInMillis) + where: 'the following parameters are used' + scenario | fetchDescendantsOption | anchor || durationLimit | expectedNumberOfDataNodes + 'no descendants' | OMIT_DESCENDANTS | 'openroadm1' || 100 | 50 + 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 400 | 50 * 2 + 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 500 | 50 * 86 + } + + def 'Query ancestors with #scenario.'() { + when: 'query data nodes (using a fresh anchor with identical data for each test)' + stopWatch.start() + def result = objectUnderTest.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchor, '//openroadm-device[@ne-state="inservice"]/ancestor::openroadm-devices', fetchDescendantsOption) + stopWatch.stop() + def durationInMillis = stopWatch.getTotalTimeMillis() + then: 'the expected number of nodes is returned' + assert countDataNodesInTree(result) == expectedNumberOfDataNodes + and: 'all data is read within #durationLimit ms' + recordAndAssertPerformance("Query ancestors with ${scenario}", durationLimit, durationInMillis) + where: 'the following parameters are used' + scenario | fetchDescendantsOption | anchor || durationLimit | expectedNumberOfDataNodes + 'no descendants' | OMIT_DESCENDANTS | 'openroadm1' || 100 | 1 + 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 250 | 1 + 50 + 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 400 | 1 + 50 * 86 + } + +} diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy index 87327030c7..443dd7efd7 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy @@ -22,7 +22,6 @@ package org.onap.cps.integration.performance.ncmp import java.util.stream.Collectors import org.onap.cps.integration.performance.base.NcmpRegistryPerfTestBase -import org.springframework.dao.DataAccessResourceFailureException import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS @@ -43,21 +42,12 @@ class CmHandleQueryPerfTest extends NcmpRegistryPerfTestBase { def result = cpsDataService.getDataNodesForMultipleXpaths(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, xpaths, INCLUDE_ALL_DESCENDANTS) stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() - then: 'the required operations are performed within 3 seconds' - recordAndAssertPerformance("CpsPath Registry attributes Query", 3_000, durationInMillis) + then: 'the required operations are performed within 1200 ms' + recordAndAssertPerformance("CpsPath Registry attributes Query", 1200, durationInMillis) and: 'all but 1 (other node) are returned' result.size() == 999 and: 'the tree contains all the expected descendants too' assert countDataNodesInTree(result) == 5 * 999 } - def 'Multiple get limit exceeded: 32,764 (~ 2^15) xpaths.'() { - given: 'more than 32,764 xpaths)' - def xpaths = (0..32_764).collect(i -> "/size/of/this/path/does/not/matter/for/limit[@id='" + i + "']") - when: 'single get is executed to get all the parent objects and their descendants' - cpsDataService.getDataNodesForMultipleXpaths(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, xpaths, INCLUDE_ALL_DESCENDANTS) - then: 'an exception is thrown' - thrown(DataAccessResourceFailureException.class) - } - } -- cgit 1.2.3-korg