diff options
author | ToineSiebelink <toine.siebelink@est.tech> | 2022-10-20 18:34:29 +0100 |
---|---|---|
committer | Toine Siebelink <toine.siebelink@est.tech> | 2022-10-27 10:52:56 +0000 |
commit | a096a7faa35b345c765102201a5a09cc03ef541a (patch) | |
tree | bcf99d79138ef5f15e681a564ff655da44f26a5a /cps-ri/src/test/groovy | |
parent | 321d969c11826ccc3a01f6002cfaae2d0a5a4f9d (diff) |
Read Performance Improvement - Using Native Query
- Native query for FragmentExtracts
- Convert FragmentExtracts to tree of FragmentEntity
- Native Query now used for all Gets with descendants (orignal hibernate option only used when descendanst ommited)
- Added error handling for not-found on native query
- Ommit descendants by default on many udpate use-cases (this might have a signifcant perf. improvemnt impact too)
- Improved legacy tests for delete use-cases
- Corrected performace test expectation
- Fix TTL test realizing TTL resolution is whole seconds!
Issue-ID: CPS-1301
Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
Change-Id: I658ac1b7b7036f01050f30bdf9e5bd175725ef1d
Diffstat (limited to 'cps-ri/src/test/groovy')
3 files changed, 48 insertions, 58 deletions
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy index 5e15ca795f..412c5aa7b9 100755 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy @@ -531,28 +531,25 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { def 'Confirm deletion of #scenario.'() { given: 'a valid data node' def dataNode - def dataNodeXpath - when: 'data nodes are deleted' + and: 'data nodes are deleted' objectUnderTest.deleteDataNode(DATASPACE_NAME, ANCHOR_NAME3, xpathForDeletion) - then: 'verify data nodes are removed' - try { - dataNode = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_NAME3, getDataNodesXpaths, INCLUDE_ALL_DESCENDANTS) - dataNodeXpath = dataNode.xpath - assert dataNodeXpath == expectedXpaths - } catch (DataNodeNotFoundException) { - assert dataNodeXpath == expectedXpaths + when: 'verify data nodes are removed' + objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_NAME3, xpathForDeletion, INCLUDE_ALL_DESCENDANTS) + then: + thrown(DataNodeNotFoundException) + and: 'some related object is not deleted' + if (xpathSurvivor!=null) { + dataNode = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_NAME3, xpathSurvivor, INCLUDE_ALL_DESCENDANTS) + assert dataNode.xpath == xpathSurvivor } where: 'following parameters were used' - scenario | xpathForDeletion | getDataNodesXpaths || expectedXpaths - 'child of target' | '/parent-206/child-206' | '/parent-206/child-206' || null - 'child data node, parent still exists' | '/parent-206/child-206' | '/parent-206' || '/parent-206' - 'list element' | '/parent-206/child-206/grand-child-206[@key="A"]' | '/parent-206/child-206/grand-child-206[@key="A"]' || null - 'list element, sibling still exists' | '/parent-206/child-206/grand-child-206[@key="A"]' | '/parent-206/child-206/grand-child-206[@key="X"]' || "/parent-206/child-206/grand-child-206[@key='X']" - 'container node' | '/parent-206' | '/parent-206' || null - 'container list node' | '/parent-206[@key="A"]' | '/parent-206[@key="B"]' || "/parent-206[@key='B']" - 'root node with xpath /' | '/' | '/' || null - 'root node with xpath passed as blank' | '' | '' || null - + scenario | xpathForDeletion || xpathSurvivor + 'child data node, parent still exists' | '/parent-206/child-206' || '/parent-206' + 'list element, sibling still exists' | '/parent-206/child-206/grand-child-206[@key="A"]' || "/parent-206/child-206/grand-child-206[@key='X']" + 'container node' | '/parent-206' || null + 'container list node' | '/parent-206[@key="A"]' || "/parent-206[@key='B']" + 'root node with xpath /' | '/' || null + 'root node with xpath passed as blank' | '' || null } @Sql([CLEAR_DATA, SET_DATA]) diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy index 3b15b7607f..b124925aa8 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy @@ -25,8 +25,7 @@ import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.cache.AnchorDataCacheEntry import org.onap.cps.spi.entities.AnchorEntity import org.onap.cps.spi.entities.FragmentEntity -import org.onap.cps.spi.entities.SchemaSetEntity -import org.onap.cps.spi.entities.YangResourceEntity +import org.onap.cps.spi.entities.FragmentExtract import org.onap.cps.spi.exceptions.ConcurrencyException import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.DataNode @@ -52,25 +51,6 @@ class CpsDataPersistenceServiceSpec extends Specification { def objectUnderTest = new CpsDataPersistenceServiceImpl( mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper, mockSessionManager, mockAnchorDataCache) - @Shared - def NEW_RESOURCE_CONTENT = 'module stores {\n' + - ' yang-version 1.1;\n' + - ' namespace "org:onap:ccsdk:sample";\n' + - '\n' + - ' prefix book-store;\n' + - '\n' + - ' revision "2020-09-15" {\n' + - ' description\n' + - ' "Sample Model";\n' + - ' }' + - '}' - - @Shared - def yangResourceSet = [new YangResourceEntity(moduleName: 'moduleName', content: NEW_RESOURCE_CONTENT, - fileName: 'sampleYangResource' - )] as Set - - def 'Handling of StaleStateException (caused by concurrent updates) during update data node and descendants.'() { given: 'the fragment repository returns a fragment entity' mockFragmentRepository.getByDataspaceAndAnchorAndXpath(*_) >> { @@ -107,16 +87,12 @@ class CpsDataPersistenceServiceSpec extends Specification { and: 'it contains the failed datanodes' assert thrown.details.contains('/node2') assert thrown.details.contains('/node3') - } + def 'Retrieving a data node with a property JSON value of #scenario'() { - given: 'a fragment with a property JSON value of #scenario' - mockFragmentRepository.getByDataspaceAndAnchorAndXpath(*_) >> { - new FragmentEntity(childFragments: Collections.emptySet(), - attributes: "{\"some attribute\": ${dataString}}", - anchor: new AnchorEntity(schemaSet: new SchemaSetEntity(yangResources: yangResourceSet ))) - } + given: 'the db has a fragment with an attribute property JSON value of #scenario' + mockFragmentWithJson("{\"some attribute\": ${dataString}}") when: 'getting the data node represented by this fragment' def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor', '/parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) @@ -140,9 +116,7 @@ class CpsDataPersistenceServiceSpec extends Specification { def 'Retrieving a data node with invalid JSON'() { given: 'a fragment with invalid JSON' - mockFragmentRepository.getByDataspaceAndAnchorAndXpath(*_) >> { - new FragmentEntity(childFragments: Collections.emptySet(), attributes: '{invalid json') - } + mockFragmentWithJson('{invalid json') when: 'getting the data node represented by this fragment' objectUnderTest.getDataNode('my-dataspace', 'my-anchor', '/parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) @@ -214,4 +188,21 @@ class CpsDataPersistenceServiceSpec extends Specification { } return dataNode } + + def mockFragmentWithJson(json) { + def anchorName = 'some anchor' + def anchorDataCacheEntry = new AnchorDataCacheEntry() + anchorDataCacheEntry.setProperty(objectUnderTest.TOP_LEVEL_MODULE_PREFIX_PROPERTY_NAME, 'some prefix') + mockAnchorDataCache.containsKey(anchorName) >> true + mockAnchorDataCache.get(anchorName) >> anchorDataCacheEntry + def mockAnchor = Mock(AnchorEntity) + mockAnchor.getId() >> 123 + mockAnchor.getName() >> anchorName + mockAnchorRepository.getByDataspaceAndName(*_) >> mockAnchor + def mockFragmentExtract = Mock(FragmentExtract) + mockFragmentExtract.getId() >> 456 + mockFragmentExtract.getAttributes() >> json + mockFragmentRepository.findByAnchorIdAndParentXpath(*_) >> [mockFragmentExtract] + } + } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsToDataNodePerfSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsToDataNodePerfSpec.groovy index 5b28028133..283be6b9b8 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsToDataNodePerfSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsToDataNodePerfSpec.groovy @@ -22,20 +22,23 @@ package org.onap.cps.ri.performance import org.apache.commons.lang3.time.StopWatch import org.onap.cps.spi.CpsDataPersistenceService +import org.onap.cps.spi.impl.CpsDataPersistenceServiceImpl 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.FragmentRepository import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.context.jdbc.Sql import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS class CpsToDataNodePerfSpec extends CpsPersistenceSpecBase { + static final String SET_DATA = '/data/fragment.sql' + @Autowired CpsDataPersistenceService objectUnderTest - static final String SET_DATA = '/data/fragment.sql' - static final String XPATH_DATA_NODE_WITH_DESCENDANTS = '/parent-1' + def XPATH_DATA_NODE_WITH_DESCENDANTS = '/parent-1' @Sql([CLEAR_DATA, SET_DATA]) def 'Get data node by xpath with all descendants with many children'() { @@ -48,14 +51,13 @@ class CpsToDataNodePerfSpec extends CpsPersistenceSpecBase { when: 'data node is requested with all descendants' def readStopWatch = new StopWatch() readStopWatch.start() - def result = objectUnderTest.getDataNode( - DATASPACE_NAME, ANCHOR_NAME1, XPATH_DATA_NODE_WITH_DESCENDANTS, INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_NAME1, XPATH_DATA_NODE_WITH_DESCENDANTS, INCLUDE_ALL_DESCENDANTS) readStopWatch.stop() def readDurationInMillis = readStopWatch.getTime() - then : 'setup duration is under 8 seconds' + then: 'setup duration is under 8 seconds' assert setupDurationInMillis < 8000 - and: 'read duration is under 6 seconds' - assert readDurationInMillis < 6000 + and: 'read duration is under 1500 milliseconds' + assert readDurationInMillis < 1500 and: 'data node is returned with all the descendants populated' assert countDataNodes(result) == 1533 } @@ -86,4 +88,4 @@ class CpsToDataNodePerfSpec extends CpsPersistenceSpecBase { } return nodeCount } -}
\ No newline at end of file +} |