From 35e5646fef7402cb922cb20ef08cfd4e032a4a2c Mon Sep 17 00:00:00 2001 From: danielhanrahan Date: Thu, 12 Jan 2023 09:55:56 +0000 Subject: Add delete performance tests - move existing delete tests to new class - add tests for deleting lists and list elements - add test for deleting root node Issue-ID: CPS-1437 Signed-off-by: danielhanrahan Change-Id: I81228aa9473ed28d550db64b28c38abb1c1016f5 --- .../cps/spi/impl/CpsPersistencePerfSpecBase.groovy | 74 ++++++++++ .../CpsDataPersistenceServiceDeletePerfTest.groovy | 154 +++++++++++++++++++++ .../CpsDataPersistenceServicePerfTest.groovy | 86 ++---------- 3 files changed, 236 insertions(+), 78 deletions(-) create mode 100644 cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistencePerfSpecBase.groovy create mode 100644 cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy (limited to 'cps-ri') diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistencePerfSpecBase.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistencePerfSpecBase.groovy new file mode 100644 index 000000000..3bbae2d08 --- /dev/null +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistencePerfSpecBase.groovy @@ -0,0 +1,74 @@ +/* + * ============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.spi.impl + +import org.onap.cps.spi.model.DataNode +import org.onap.cps.spi.model.DataNodeBuilder + +class CpsPersistencePerfSpecBase extends CpsPersistenceSpecBase { + + static final String PERF_TEST_DATA = '/data/perf-test.sql' + static final String PERF_DATASPACE = 'PERF-DATASPACE' + static final String PERF_ANCHOR = 'PERF-ANCHOR' + static final String PERF_TEST_PARENT = '/perf-parent-1' + + static def xpathsToAllGrandChildren = [] + + def createLineage(cpsDataPersistenceService, numberOfChildren, numberOfGrandChildren, createLists) { + xpathsToAllGrandChildren = [] + (1..numberOfChildren).each { + if (createLists) { + def xpathFormat = "${PERF_TEST_PARENT}/perf-test-list-${it}[@key='%d']" + def listElements = goForthAndMultiply(xpathFormat, numberOfGrandChildren) + cpsDataPersistenceService.addListElements(PERF_DATASPACE, PERF_ANCHOR, PERF_TEST_PARENT, listElements) + } else { + def xpathFormat = "${PERF_TEST_PARENT}/perf-test-child-${it}/perf-test-grand-child-%d" + def grandChildren = goForthAndMultiply(xpathFormat, numberOfGrandChildren) + def child = new DataNodeBuilder() + .withXpath("${PERF_TEST_PARENT}/perf-test-child-${it}") + .withChildDataNodes(grandChildren) + .build() + cpsDataPersistenceService.addChildDataNode(PERF_DATASPACE, PERF_ANCHOR, PERF_TEST_PARENT, child) + } + } + } + + def goForthAndMultiply(xpathFormat, numberOfGrandChildren) { + def grandChildren = [] + (1..numberOfGrandChildren).each { + def xpath = String.format(xpathFormat as String, it) + def grandChild = new DataNodeBuilder().withXpath(xpath).build() + xpathsToAllGrandChildren.add(grandChild.xpath) + grandChildren.add(grandChild) + } + return grandChildren + } + + def countDataNodes(dataNodes) { + int nodeCount = 1 + for (DataNode parent : dataNodes) { + for (DataNode child : parent.childDataNodes) { + nodeCount = nodeCount + (countDataNodes(child)) + } + } + return nodeCount + } +} diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy new file mode 100644 index 000000000..5aae285d7 --- /dev/null +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy @@ -0,0 +1,154 @@ +/* + * ============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.spi.performance + +import org.onap.cps.spi.CpsDataPersistenceService +import org.onap.cps.spi.impl.CpsPersistencePerfSpecBase +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.test.context.jdbc.Sql +import org.springframework.util.StopWatch + +import java.util.concurrent.TimeUnit + +class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase { + + @Autowired + CpsDataPersistenceService objectUnderTest + + static def NUMBER_OF_CHILDREN = 100 + static def NUMBER_OF_GRAND_CHILDREN = 50 + static def NUMBER_OF_LISTS = 100 + static def NUMBER_OF_LIST_ELEMENTS = 50 + static def ALLOWED_SETUP_TIME_MS = TimeUnit.SECONDS.toMillis(10) + + def stopWatch = new StopWatch() + + @Sql([CLEAR_DATA, PERF_TEST_DATA]) + def 'Create a node with many descendants (please note, subsequent tests depend on this running first).'() { + given: 'a node with a large number of descendants is created' + stopWatch.start() + createLineage(objectUnderTest, NUMBER_OF_CHILDREN, NUMBER_OF_GRAND_CHILDREN, false) + stopWatch.stop() + def setupDurationInMillis = stopWatch.getTotalTimeMillis() + and: 'setup duration is under #ALLOWED_SETUP_TIME_MS milliseconds' + assert setupDurationInMillis < ALLOWED_SETUP_TIME_MS + } + + def 'Delete 5 children with grandchildren'() { + when: 'child nodes are deleted' + stopWatch.start() + (1..5).each { + def childPath = "${PERF_TEST_PARENT}/perf-test-child-${it}".toString(); + objectUnderTest.deleteDataNode(PERF_DATASPACE, PERF_ANCHOR, childPath) + } + stopWatch.stop() + def deleteDurationInMillis = stopWatch.getTotalTimeMillis() + then: 'delete duration is under 6000 milliseconds' + assert deleteDurationInMillis < 6000 + } + + def 'Delete 50 grandchildren (that have no descendants)'() { + when: 'target nodes are deleted' + stopWatch.start() + (1..50).each { + def grandchildPath = "${PERF_TEST_PARENT}/perf-test-child-6/perf-test-grand-child-${it}".toString(); + objectUnderTest.deleteDataNode(PERF_DATASPACE, PERF_ANCHOR, grandchildPath) + } + stopWatch.stop() + def deleteDurationInMillis = stopWatch.getTotalTimeMillis() + then: 'delete duration is under 500 milliseconds' + assert deleteDurationInMillis < 500 + } + + def 'Delete 1 large data node with many descendants'() { + when: 'parent node is deleted' + stopWatch.start() + objectUnderTest.deleteDataNode(PERF_DATASPACE, PERF_ANCHOR, PERF_TEST_PARENT) + stopWatch.stop() + def deleteDurationInMillis = stopWatch.getTotalTimeMillis() + then: 'delete duration is under 2500 milliseconds' + assert deleteDurationInMillis < 2500 + } + + @Sql([CLEAR_DATA, PERF_TEST_DATA]) + def 'Create a node with many list elements (please note, subsequent tests depend on this running first).'() { + given: 'a node with a large number of descendants is created' + stopWatch.start() + createLineage(objectUnderTest, NUMBER_OF_LISTS, NUMBER_OF_LIST_ELEMENTS, true) + stopWatch.stop() + def setupDurationInMillis = stopWatch.getTotalTimeMillis() + and: 'setup duration is under #ALLOWED_SETUP_TIME_MS milliseconds' + assert setupDurationInMillis < ALLOWED_SETUP_TIME_MS + } + + def 'Delete 5 whole lists with many elements'() { + when: 'list nodes are deleted' + stopWatch.start() + (1..5).each { + def childPath = "${PERF_TEST_PARENT}/perf-test-list-${it}".toString(); + objectUnderTest.deleteListDataNode(PERF_DATASPACE, PERF_ANCHOR, childPath) + } + stopWatch.stop() + def deleteDurationInMillis = stopWatch.getTotalTimeMillis() + then: 'delete duration is under 4000 milliseconds' + assert deleteDurationInMillis < 4000 + } + + def 'Delete 10 list elements with keys'() { + when: 'list elements are deleted' + stopWatch.start() + (1..10).each { + def key = it.toString() + def grandchildPath = "${PERF_TEST_PARENT}/perf-test-list-6[@key='${key}']" + objectUnderTest.deleteListDataNode(PERF_DATASPACE, PERF_ANCHOR, grandchildPath) + } + stopWatch.stop() + def deleteDurationInMillis = stopWatch.getTotalTimeMillis() + then: 'delete duration is under 6000 milliseconds' + assert deleteDurationInMillis < 6000 + } + + @Sql([CLEAR_DATA, PERF_TEST_DATA]) + def 'Delete root node with many descendants'() { + given: 'a node with a large number of descendants is created' + createLineage(objectUnderTest, NUMBER_OF_CHILDREN, NUMBER_OF_GRAND_CHILDREN, false) + when: 'root node is deleted' + stopWatch.start() + objectUnderTest.deleteDataNode(PERF_DATASPACE, PERF_ANCHOR, '/') + stopWatch.stop() + def deleteDurationInMillis = stopWatch.getTotalTimeMillis() + then: 'delete duration is under 250 milliseconds' + assert deleteDurationInMillis < 250 + } + + @Sql([CLEAR_DATA, PERF_TEST_DATA]) + def 'Delete data nodes for an anchor'() { + given: 'a node with a large number of descendants is created' + createLineage(objectUnderTest, NUMBER_OF_CHILDREN, NUMBER_OF_GRAND_CHILDREN, false) + when: 'data nodes are deleted' + stopWatch.start() + objectUnderTest.deleteDataNodes(PERF_DATASPACE, PERF_ANCHOR) + stopWatch.stop() + def deleteDurationInMillis = stopWatch.getTotalTimeMillis() + then: 'delete duration is under 250 milliseconds' + assert deleteDurationInMillis < 250 + } +} diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy index 45cdb4d07..28363d74b 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-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. @@ -20,11 +20,9 @@ package org.onap.cps.spi.performance +import org.onap.cps.spi.impl.CpsPersistencePerfSpecBase import org.springframework.util.StopWatch import org.onap.cps.spi.CpsDataPersistenceService -import org.onap.cps.spi.impl.CpsPersistenceSpecBase -import org.onap.cps.spi.model.DataNode -import org.onap.cps.spi.model.DataNodeBuilder import org.onap.cps.spi.repository.AnchorRepository import org.onap.cps.spi.repository.DataspaceRepository import org.onap.cps.spi.repository.FragmentRepository @@ -36,9 +34,7 @@ import java.util.concurrent.TimeUnit import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS -class CpsDataPersistenceServicePerfTest extends CpsPersistenceSpecBase { - - static final String PERF_TEST_DATA = '/data/perf-test.sql' +class CpsDataPersistenceServicePerfTest extends CpsPersistencePerfSpecBase { @Autowired CpsDataPersistenceService objectUnderTest @@ -52,7 +48,6 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistenceSpecBase { @Autowired FragmentRepository fragmentRepository - static def PERF_TEST_PARENT = '/perf-parent-1' static def NUMBER_OF_CHILDREN = 200 static def NUMBER_OF_GRAND_CHILDREN = 50 static def TOTAL_NUMBER_OF_NODES = 1 + NUMBER_OF_CHILDREN + (NUMBER_OF_CHILDREN * NUMBER_OF_GRAND_CHILDREN) // Parent + Children + Grand-children @@ -61,13 +56,12 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistenceSpecBase { def stopWatch = new StopWatch() def readStopWatch = new StopWatch() - static def xpathsToAllGrandChildren = [] @Sql([CLEAR_DATA, PERF_TEST_DATA]) def 'Create a node with many descendants (please note, subsequent tests depend on this running first).'() { given: 'a node with a large number of descendants is created' stopWatch.start() - createLineage() + createLineage(objectUnderTest, NUMBER_OF_CHILDREN, NUMBER_OF_GRAND_CHILDREN, false) stopWatch.stop() def setupDurationInMillis = stopWatch.getTotalTimeMillis() and: 'setup duration is under #ALLOWED_SETUP_TIME_MS milliseconds' @@ -77,7 +71,7 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistenceSpecBase { def 'Get data node with many descendants by xpath #scenario'() { when: 'get parent is executed with all descendants' stopWatch.start() - def result = objectUnderTest.getDataNode('PERF-DATASPACE', 'PERF-ANCHOR', xpath, INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.getDataNode(PERF_DATASPACE, PERF_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) stopWatch.stop() def readDurationInMillis = stopWatch.getTotalTimeMillis() then: 'read duration is under 500 milliseconds' @@ -93,7 +87,7 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistenceSpecBase { def 'Query parent data node with many descendants by cps-path'() { when: 'query is executed with all descendants' stopWatch.start() - def result = objectUnderTest.queryDataNodes('PERF-DATASPACE', 'PERF-ANCHOR', '//perf-parent-1' , INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.queryDataNodes(PERF_DATASPACE, PERF_ANCHOR, '//perf-parent-1' , INCLUDE_ALL_DESCENDANTS) stopWatch.stop() def readDurationInMillis = stopWatch.getTotalTimeMillis() then: 'read duration is under 500 milliseconds' @@ -106,7 +100,7 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistenceSpecBase { when: 'we query for all grandchildren (except 1 for fun) with the new native method' xpathsToAllGrandChildren.remove(0) readStopWatch.start() - def result = objectUnderTest.getDataNodes('PERF-DATASPACE', 'PERF-ANCHOR', xpathsToAllGrandChildren, INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.getDataNodes(PERF_DATASPACE, PERF_ANCHOR, xpathsToAllGrandChildren, INCLUDE_ALL_DESCENDANTS) readStopWatch.stop() def readDurationInMillis = readStopWatch.getTotalTimeMillis() then: 'the returned number of entities equal to the number of children * number of grandchildren' @@ -118,7 +112,7 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistenceSpecBase { def 'Query many descendants by cps-path with #scenario'() { when: 'query is executed with all descendants' stopWatch.start() - def result = objectUnderTest.queryDataNodes('PERF-DATASPACE', 'PERF-ANCHOR', '//perf-test-grand-child-1', descendantsOption) + def result = objectUnderTest.queryDataNodes(PERF_DATASPACE, PERF_ANCHOR, '//perf-test-grand-child-1', descendantsOption) stopWatch.stop() def readDurationInMillis = stopWatch.getTotalTimeMillis() then: 'read duration is under 500 milliseconds' @@ -130,68 +124,4 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistenceSpecBase { 'omit descendants ' | OMIT_DESCENDANTS || 150 'include descendants (although there are none)' | INCLUDE_ALL_DESCENDANTS || 150 } - - def 'Delete 50 grandchildren (that have no descendants)'() { - when: 'target nodes are deleted' - stopWatch.start() - (1..NUMBER_OF_GRAND_CHILDREN).each { - def grandchildPath = "${PERF_TEST_PARENT}/perf-test-child-1/perf-test-grand-child-${it}".toString(); - objectUnderTest.deleteDataNode('PERF-DATASPACE', 'PERF-ANCHOR', grandchildPath) - } - stopWatch.stop() - def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 1000 milliseconds' - assert deleteDurationInMillis < 1000 - } - - def 'Delete 5 children with grandchildren'() { - when: 'child nodes are deleted' - stopWatch.start() - (1..5).each { - def childPath = "${PERF_TEST_PARENT}/perf-test-child-${it}".toString(); - objectUnderTest.deleteDataNode('PERF-DATASPACE', 'PERF-ANCHOR', childPath) - } - stopWatch.stop() - def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 10000 milliseconds' - assert deleteDurationInMillis < 10000 - } - - def 'Delete 1 large data node with many descendants'() { - when: 'parent node is deleted' - stopWatch.start() - objectUnderTest.deleteDataNode('PERF-DATASPACE', 'PERF-ANCHOR', PERF_TEST_PARENT) - stopWatch.stop() - def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 5000 milliseconds' - assert deleteDurationInMillis < 5000 - } - - def createLineage() { - (1..NUMBER_OF_CHILDREN).each { - def childName = "perf-test-child-${it}".toString() - def child = goForthAndMultiply(PERF_TEST_PARENT, childName) - objectUnderTest.addChildDataNode('PERF-DATASPACE', 'PERF-ANCHOR', PERF_TEST_PARENT, child) - } - } - - def goForthAndMultiply(parentXpath, childName) { - def grandChildren = [] - (1..NUMBER_OF_GRAND_CHILDREN).each { - def grandChild = new DataNodeBuilder().withXpath("${parentXpath}/${childName}/perf-test-grand-child-${it}").build() - xpathsToAllGrandChildren.add(grandChild.xpath) - grandChildren.add(grandChild) - } - return new DataNodeBuilder().withXpath("${parentXpath}/${childName}").withChildDataNodes(grandChildren).build() - } - - def countDataNodes(dataNodes) { - int nodeCount = 1 - for (DataNode parent : dataNodes) { - for (DataNode child : parent.childDataNodes) { - nodeCount = nodeCount + (countDataNodes(child)) - } - } - return nodeCount - } } -- cgit 1.2.3-korg