summaryrefslogtreecommitdiffstats
path: root/cps-ri
diff options
context:
space:
mode:
Diffstat (limited to 'cps-ri')
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java24
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java10
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java10
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy8
-rwxr-xr-xcps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy19
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy124
-rw-r--r--cps-ri/src/test/java/org/onap/cps/TestApplication.java2
-rw-r--r--cps-ri/src/test/resources/application.yml2
8 files changed, 170 insertions, 29 deletions
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
index 51b2482953..5b89d9f4c4 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
@@ -24,9 +24,9 @@ package org.onap.cps.spi.impl;
import java.util.Collection;
import java.util.List;
-import java.util.Set;
import java.util.stream.Collectors;
import javax.transaction.Transactional;
+import lombok.AllArgsConstructor;
import org.onap.cps.spi.CpsAdminPersistenceService;
import org.onap.cps.spi.entities.AnchorEntity;
import org.onap.cps.spi.entities.DataspaceEntity;
@@ -38,30 +38,19 @@ import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException;
import org.onap.cps.spi.model.Anchor;
import org.onap.cps.spi.repository.AnchorRepository;
import org.onap.cps.spi.repository.DataspaceRepository;
-import org.onap.cps.spi.repository.FragmentRepository;
import org.onap.cps.spi.repository.SchemaSetRepository;
import org.onap.cps.spi.repository.YangResourceRepository;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Component;
@Component
+@AllArgsConstructor
public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceService {
- @Autowired
- private DataspaceRepository dataspaceRepository;
-
- @Autowired
- private AnchorRepository anchorRepository;
-
- @Autowired
- private SchemaSetRepository schemaSetRepository;
-
- @Autowired
- private FragmentRepository fragmentRepository;
-
- @Autowired
- private YangResourceRepository yangResourceRepository;
+ private final DataspaceRepository dataspaceRepository;
+ private final AnchorRepository anchorRepository;
+ private final SchemaSetRepository schemaSetRepository;
+ private final YangResourceRepository yangResourceRepository;
@Override
public void createDataspace(final String dataspaceName) {
@@ -140,7 +129,6 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
@Override
public void deleteAnchor(final String dataspaceName, final String anchorName) {
final var anchorEntity = getAnchorEntity(dataspaceName, anchorName);
- fragmentRepository.deleteByAnchorIn(Set.of(anchorEntity));
anchorRepository.delete(anchorEntity);
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
index 2d67d7c953..ed414fc30c 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2021-2022 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
- * Modifications Copyright (C) 2020-2021 Bell Canada.
+ * Modifications Copyright (C) 2020-2022 Bell Canada.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -320,6 +320,14 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
fragmentRepository.save(parentEntity);
}
+ @Override
+ @Transactional
+ public void deleteDataNodes(final String dataspaceName, final String anchorName) {
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ anchorRepository.findByDataspaceAndName(dataspaceEntity, anchorName)
+ .ifPresent(
+ anchorEntity -> fragmentRepository.deleteByAnchorIn(Set.of(anchorEntity)));
+ }
@Override
@Transactional
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
index 3e39a05c51..86d5de6d0f 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
@@ -134,10 +134,10 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
@Transactional
// A retry is made to store the schema set if it fails because of duplicated yang resource exception that
// can occur in case of specific concurrent requests.
- @Retryable(value = DuplicatedYangResourceException.class, maxAttempts = 2, backoff = @Backoff(delay = 500))
+ @Retryable(value = DuplicatedYangResourceException.class, maxAttempts = 5, backoff =
+ @Backoff(random = true, delay = 200, maxDelay = 2000, multiplier = 2))
public void storeSchemaSet(final String dataspaceName, final String schemaSetName,
final Map<String, String> yangResourcesNameToContentMap) {
-
final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
final var yangResourceEntities = synchronizeYangResources(yangResourcesNameToContentMap);
final var schemaSetEntity = new SchemaSetEntity();
@@ -153,6 +153,10 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
@Override
@Transactional
+ // A retry is made to store the schema set if it fails because of duplicated yang resource exception that
+ // can occur in case of specific concurrent requests.
+ @Retryable(value = DuplicatedYangResourceException.class, maxAttempts = 5, backoff =
+ @Backoff(random = true, delay = 200, maxDelay = 2000, multiplier = 2))
public void storeSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
final Map<String, String> newYangResourcesModuleNameToContentMap,
final List<ModuleReference> moduleReferences) {
@@ -219,7 +223,7 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
convertedException.ifPresent(
e -> log.warn(
"Cannot persist duplicated yang resource. "
- + "A total of 2 attempts to store the schema set are planned.", e));
+ + "System will attempt this method up to 5 times.", e));
throw convertedException.isPresent() ? convertedException.get() : dataIntegrityViolationException;
}
}
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy
index 2218014ec1..063bd5b5ae 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy
@@ -41,8 +41,7 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
static final String SET_DATA = '/data/anchor.sql'
static final String SAMPLE_DATA_FOR_ANCHORS_WITH_MODULES = '/data/anchors-schemaset-modules.sql'
static final String DATASPACE_WITH_NO_DATA = 'DATASPACE-002-NO-DATA'
- static final Integer DELETED_ANCHOR_ID = 3001
- static final Long DELETED_FRAGMENT_ID = 4001
+ static final Integer DELETED_ANCHOR_ID = 3002
@Sql(CLEAR_DATA)
def 'Create and retrieve a new dataspace.'() {
@@ -150,10 +149,9 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
@Sql([CLEAR_DATA, SET_DATA])
def 'Delete anchor'() {
when: 'delete anchor action is invoked'
- objectUnderTest.deleteAnchor(DATASPACE_NAME, ANCHOR_NAME1)
- then: 'anchor and associated data fragment are deleted'
+ objectUnderTest.deleteAnchor(DATASPACE_NAME, ANCHOR_NAME2)
+ then: 'anchor is deleted'
assert anchorRepository.findById(DELETED_ANCHOR_ID).isEmpty()
- assert fragmentRepository.findById(DELETED_FRAGMENT_ID).isEmpty()
}
@Sql([CLEAR_DATA, SET_DATA])
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 60d7774cca..41e7a419a5 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
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2021-2022 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
- * Modifications Copyright (C) 2021 Bell Canada.
+ * Modifications Copyright (C) 2021-2022 Bell Canada.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,6 @@ import org.onap.cps.spi.model.DataNodeBuilder
import org.onap.cps.utils.JsonObjectMapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.jdbc.Sql
-
import javax.validation.ConstraintViolationException
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
@@ -49,6 +48,8 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
static final JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
static final String SET_DATA = '/data/fragment.sql'
+ static final int DATASPACE_1001_ID = 1001L
+ static final int ANCHOR_3003_ID = 3003L
static final long ID_DATA_NODE_WITH_DESCENDANTS = 4001
static final String XPATH_DATA_NODE_WITH_DESCENDANTS = '/parent-1'
static final String XPATH_DATA_NODE_WITH_LEAVES = '/parent-100'
@@ -524,6 +525,20 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
'invalid list element' | '/parent-206/child-206/grand-child-206@key="A"]'
}
+ @Sql([CLEAR_DATA, SET_DATA])
+ def 'Delete data node for an anchor.'() {
+ given: 'a data-node exists for an anchor'
+ assert fragmentsExistInDB(DATASPACE_1001_ID, ANCHOR_3003_ID)
+ when: 'data nodes are deleted '
+ objectUnderTest.deleteDataNodes(DATASPACE_NAME, ANCHOR_NAME3)
+ then: 'all data-nodes are deleted successfully'
+ assert !fragmentsExistInDB(DATASPACE_1001_ID, ANCHOR_3003_ID)
+ }
+
+ def fragmentsExistInDB(dataSpaceId, anchorId) {
+ !fragmentRepository.findRootsByDataspaceAndAnchor(dataSpaceId, anchorId).isEmpty()
+ }
+
static Collection<DataNode> toDataNodes(xpaths) {
return xpaths.collect { new DataNodeBuilder().withXpath(it).build() }
}
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy
new file mode 100644
index 0000000000..085bb3340b
--- /dev/null
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy
@@ -0,0 +1,124 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Bell Canada.
+ * ================================================================================
+ * 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 com.fasterxml.jackson.databind.ObjectMapper
+import org.hibernate.exception.ConstraintViolationException
+import org.mockito.InjectMocks
+import org.mockito.Mock
+import org.onap.cps.spi.CpsAdminPersistenceService
+import org.onap.cps.spi.CpsModulePersistenceService
+import org.onap.cps.spi.entities.DataspaceEntity
+import org.onap.cps.spi.entities.YangResourceEntity
+import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
+import org.onap.cps.spi.model.ExtendedModuleReference
+import org.onap.cps.spi.model.ModuleReference
+import org.onap.cps.spi.repository.AnchorRepository
+import org.onap.cps.spi.repository.DataspaceRepository
+import org.onap.cps.spi.repository.FragmentRepository
+import org.onap.cps.spi.repository.SchemaSetRepository
+import org.onap.cps.spi.repository.YangResourceRepository
+import org.onap.cps.utils.JsonObjectMapper
+import org.spockframework.spring.SpringBean
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.boot.test.mock.mockito.MockBean
+import org.springframework.dao.DataIntegrityViolationException
+import org.springframework.test.context.jdbc.Sql
+import spock.lang.Shared
+import spock.lang.Specification
+
+import java.sql.SQLException
+
+class CpsModulePersistenceServiceConcurrencySpec extends CpsPersistenceSpecBase {
+
+ @Autowired
+ CpsModulePersistenceService objectUnderTest
+
+ @Autowired
+ AnchorRepository anchorRepository
+
+ @Autowired
+ SchemaSetRepository schemaSetRepository
+
+ @Autowired
+ CpsAdminPersistenceService cpsAdminPersistenceService
+
+ @SpringBean
+ YangResourceRepository yangResourceRepositoryMock = Mock()
+
+ @SpringBean
+ DataspaceRepository dataspaceRepositoryMock = Mock()
+
+ @SpringBean
+ JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+
+ static final String DATASPACE_NAME = 'DATASPACE-001'
+ static final String SCHEMA_SET_NAME_NEW = 'SCHEMA-SET-NEW'
+ static final String NEW_RESOURCE_NAME = 'some new resource'
+ static final String NEW_RESOURCE_CONTENT = 'module stores {\n' +
+ ' yang-version 1.1;\n' +
+ ' namespace "org:onap:ccsdk:sample";\n' +
+ '}'
+
+ def newYangResourcesNameToContentMap = [(NEW_RESOURCE_NAME):NEW_RESOURCE_CONTENT]
+
+ @Shared
+ yangResourceChecksum = 'b13faef573ed1374139d02c40d8ce09c80ea1dc70e63e464c1ed61568d48d539'
+
+ @Shared
+ yangResourceChecksumDbConstraint = 'yang_resource_checksum_key'
+
+ @Shared
+ sqlExceptionMessage = String.format('(checksum)=(%s)', yangResourceChecksum)
+
+ @Shared
+ checksumIntegrityException =
+ new DataIntegrityViolationException("checksum integrity exception",
+ new ConstraintViolationException('', new SQLException(sqlExceptionMessage), yangResourceChecksumDbConstraint))
+
+ def 'Store new schema set, retry mechanism'() {
+ given: 'no pre-existing schemaset in database'
+ dataspaceRepositoryMock.getByName(_) >> new DataspaceEntity()
+ yangResourceRepositoryMock.findAllByChecksumIn(_) >> Collections.emptyList()
+ when: 'a new schemaset is stored'
+ objectUnderTest.storeSchemaSet(DATASPACE_NAME, SCHEMA_SET_NAME_NEW, newYangResourcesNameToContentMap)
+ then: ' duplicated yang resource exception is thrown '
+ def e = thrown(DuplicatedYangResourceException)
+ and: 'the system will attempt to save the data 5 times (because checksum integrity exception is thrown each time)'
+ 5 * yangResourceRepositoryMock.saveAll(_) >> { throw checksumIntegrityException }
+ }
+
+ def 'Store schema set using modules, retry mechanism'() {
+ given: 'map of new modules, a list of existing modules, module reference'
+ def mapOfNewModules = [newModule1: 'module newmodule { yang-version 1.1; revision "2021-10-12" { } }']
+ def moduleReferenceForExistingModule = new ModuleReference("test","2021-10-12")
+ def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule]
+ and: 'no pre-existing schemaset in database'
+ dataspaceRepositoryMock.getByName(_) >> new DataspaceEntity()
+ yangResourceRepositoryMock.findAllByChecksumIn(_) >> Collections.emptyList()
+ when: 'a new schemaset is stored from a module'
+ objectUnderTest.storeSchemaSetFromModules(DATASPACE_NAME, "newSchemaSetName" , mapOfNewModules, listOfExistingModulesModuleReference)
+ then: ' duplicated yang resource exception is thrown '
+ def e = thrown(DuplicatedYangResourceException)
+ and: 'the system will attempt to save the data 5 times (because checksum integrity exception is thrown each time)'
+ 5 * yangResourceRepositoryMock.saveAll(_) >> { throw checksumIntegrityException }
+ }
+}
diff --git a/cps-ri/src/test/java/org/onap/cps/TestApplication.java b/cps-ri/src/test/java/org/onap/cps/TestApplication.java
index 0d1df456ee..075a241fc7 100644
--- a/cps-ri/src/test/java/org/onap/cps/TestApplication.java
+++ b/cps-ri/src/test/java/org/onap/cps/TestApplication.java
@@ -21,11 +21,13 @@
package org.onap.cps;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.retry.annotation.EnableRetry;
/**
* The @SpringBootApplication annotated class is required in order to run tests
* marked with @SpringBootTest annotation.
*/
@SpringBootApplication(scanBasePackages = "org.onap.cps.spi")
+@EnableRetry
public class TestApplication {
}
diff --git a/cps-ri/src/test/resources/application.yml b/cps-ri/src/test/resources/application.yml
index 73292bbfaf..18e6ed6243 100644
--- a/cps-ri/src/test/resources/application.yml
+++ b/cps-ri/src/test/resources/application.yml
@@ -18,10 +18,12 @@
spring:
jpa:
ddl-auto: create
+ show-sql: true
properties:
hibernate:
enable_lazy_load_no_trans: true
dialect: org.hibernate.dialect.PostgreSQLDialect
+ format_sql: true
datasource:
url: ${DB_URL}