aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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/CpsModulePersistenceServiceConcurrencySpec.groovy124
-rw-r--r--cps-ri/src/test/java/org/onap/cps/TestApplication.java2
-rwxr-xr-xdocs/release-notes.rst1
4 files changed, 134 insertions, 3 deletions
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/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/docs/release-notes.rst b/docs/release-notes.rst
index 058f6baaeb..3fb0a70157 100755
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -41,6 +41,7 @@ Bug Fixes
- `CPS-783 <https://jira.onap.org/browse/CPS-783>`_ Remove cm handle does not completely remove all cm handle information
- `CPS-841 <https://jira.onap.org/browse/CPS-841>`_ Upgrade log4j to 2.17.1 as recommended by ONAP SECCOM
- `CPS-867 <https://jira.onap.org/browse/CPS-867>`_ Database port made configurable through env variable DB_PORT
+ - `CPS-856 <https://jira.onap.org/browse/CPS-856>`_ Retry mechanism not working for concurrent CmHandle registration
Known Limitations, Issues and Workarounds
-----------------------------------------