From dc3cab8e93d1bc3517184e4c415b1dda24306f1c Mon Sep 17 00:00:00 2001 From: FrancescoFioraEst Date: Tue, 6 Jan 2026 13:17:37 +0000 Subject: [PATCH] Fix Duplicate key value violation error in ACM-R Issue-ID: POLICY-5530 Change-Id: I9486dfc04b528bca9b3afce233edfc31a32c26b8 Signed-off-by: FrancescoFioraEst --- .../acm/persistence/provider/MessageProvider.java | 12 ++----- .../persistence/provider/MessageProviderTest.java | 13 +------ .../runtime/supervision/SupervisionScanner.java | 24 +++++++++++-- .../runtime/liquibase/HibernateValidationTest.java | 30 +++++++++++++--- .../supervision/SupervisionScannerTest.java | 40 ++++++++++++++++++++++ 5 files changed, 90 insertions(+), 29 deletions(-) diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/MessageProvider.java b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/MessageProvider.java index 9354ce2c6..5f9c29c4d 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/MessageProvider.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/MessageProvider.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2025-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -233,14 +233,8 @@ public class MessageProvider { return Optional.empty(); } var job = new JpaMessageJob(identificationId.toString()); - try { - var result = messageJobRepository.save(job); - return Optional.of(result.getJobId()); - } catch (RuntimeException ex) { - // already exist a job with this identificationId - LOGGER.warn(ex.getMessage()); - } - return Optional.empty(); + var result = messageJobRepository.save(job); + return Optional.of(result.getJobId()); } /** diff --git a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/MessageProviderTest.java b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/MessageProviderTest.java index fe8c728ab..51c06d48f 100644 --- a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/MessageProviderTest.java +++ b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/MessageProviderTest.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2025-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; -import org.hibernate.exception.ConstraintViolationException; import org.junit.jupiter.api.Test; import org.onap.policy.clamp.models.acm.concepts.AcElementDeployAck; import org.onap.policy.clamp.models.acm.concepts.AcTypeState; @@ -235,16 +234,6 @@ class MessageProviderTest { assertThat(opt).isEmpty(); } - @Test - void testCreateJobFail() { - var messageJobRepository = mock(MessageJobRepository.class); - var identificationId = UUID.randomUUID(); - when(messageJobRepository.save(any())).thenThrow(new ConstraintViolationException("", null, "")); - var messageProvider = new MessageProvider(mock(MessageRepository.class), messageJobRepository); - var opt = messageProvider.createJob(identificationId); - assertThat(opt).isEmpty(); - } - @Test void testRemoveJob() { var messageJobRepository = mock(MessageJobRepository.class); diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java index 355d20e99..cd5e28baf 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021-2025 Nordix Foundation. + * Copyright (C) 2021-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved. * ================================================================================ @@ -24,6 +24,7 @@ package org.onap.policy.clamp.acm.runtime.supervision; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.UUID; import lombok.RequiredArgsConstructor; import org.onap.policy.clamp.acm.runtime.supervision.scanner.MonitoringScanner; @@ -33,6 +34,7 @@ import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositi import org.onap.policy.clamp.models.acm.persistence.provider.MessageProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Component; /** @@ -72,7 +74,7 @@ public class SupervisionScanner { } private void scanAcDefinition(UUID compositionId) { - var optJobId = messageProvider.createJob(compositionId); + var optJobId = createJob(compositionId); if (optJobId.isEmpty()) { return; } @@ -82,11 +84,27 @@ public class SupervisionScanner { private void scanAutomationComposition(UUID instanceId, Map acDefinitionMap) { - var optJobId = messageProvider.createJob(instanceId); + var optJobId = createJob(instanceId); if (optJobId.isEmpty()) { return; } monitoringScanner.scanAutomationComposition(instanceId, acDefinitionMap); messageProvider.removeJob(optJobId.get()); } + + /** + * Create new Job related to the identificationId. + * + * @param identificationId the instanceId or compositionId + * + * @return the jobId if the job has been created or empty if identificationId is already used + */ + public Optional createJob(UUID identificationId) { + try { + return messageProvider.createJob(identificationId); + } catch (DataIntegrityViolationException ex) { + LOGGER.debug("Job with this identificationId {} already exists", identificationId); + } + return Optional.empty(); + } } diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/liquibase/HibernateValidationTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/liquibase/HibernateValidationTest.java index b72a3b6fc..b6104c041 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/liquibase/HibernateValidationTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/liquibase/HibernateValidationTest.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2025-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,14 @@ package org.onap.policy.clamp.acm.runtime.liquibase; -import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; +import org.onap.policy.clamp.acm.runtime.supervision.SupervisionScanner; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; @@ -42,6 +48,11 @@ import org.testcontainers.junit.jupiter.Testcontainers; @Testcontainers class HibernateValidationTest extends AbstractLiquibaseTestBase { + @Autowired + private SupervisionScanner scanner; + + private static final AtomicInteger parallelCount = new AtomicInteger(); + @DynamicPropertySource static void overrideProperties(DynamicPropertyRegistry registry) { registry.add("spring.datasource.url", postgres::getJdbcUrl); @@ -50,9 +61,18 @@ class HibernateValidationTest extends AbstractLiquibaseTestBase { registry.add("spring.datasource.driver-class-name", postgres::getDriverClassName); } - // Dummy test: Hibernate validation runs during context startup and throws exception on validation failure @Test - void contextStartsAndHibernateValidationPasses() { - Assertions.assertTrue(true); + void createJobTest() { + var list = List.of(1, 2, 3, 4, 5); + var id = UUID.randomUUID(); + list.stream().parallel().forEach( + x -> { + var optJob = scanner.createJob(id); + if (optJob.isPresent()) { + parallelCount.getAndIncrement(); + } + } + ); + assertEquals(1, parallelCount.get()); } } diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java index 00512380e..6502c9849 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java @@ -65,6 +65,7 @@ import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvide import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider; import org.onap.policy.clamp.models.acm.persistence.provider.MessageProvider; import org.onap.policy.clamp.models.acm.utils.TimestampHelper; +import org.springframework.dao.DataIntegrityViolationException; class SupervisionScannerTest { @@ -291,6 +292,45 @@ class SupervisionScannerTest { verify(phaseScanner, times(0)).scanWithPhase(any(), any(), any()); } + @Test + void testScannerJobFail() { + var automationComposition = new AutomationComposition(); + automationComposition.setInstanceId(INSTANCE_ID); + automationComposition.setCompositionId(COMPOSITION_ID); + automationComposition.setDeployState(DeployState.DEPLOYING); + Set set = new HashSet<>(); + set.add(automationComposition.getInstanceId()); + var automationCompositionProvider = mock(AutomationCompositionProvider.class); + when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(set); + when(automationCompositionProvider.findAutomationComposition(automationComposition.getInstanceId())) + .thenReturn(Optional.of(automationComposition)); + + var stageScanner = mock(StageScanner.class); + var simpleScanner = mock(SimpleScanner.class); + when(simpleScanner.scanMessage(any(), any())).thenReturn(new UpdateSync()); + var phaseScanner = mock(PhaseScanner.class); + + var messageProvider = mock(MessageProvider.class); + when(messageProvider.createJob(automationComposition.getInstanceId())) + .thenThrow(new DataIntegrityViolationException("", null)); + var message = new DocMessage(); + when(messageProvider.getAllMessages(INSTANCE_ID)).thenReturn(List.of(message)); + when(messageProvider.findInstanceMessages()).thenReturn(Set.of(INSTANCE_ID)); + + var acDefinitionProvider = createAcDefinitionProvider(AcTypeState.PRIMED); + var monitoringScanner = new MonitoringScanner(automationCompositionProvider, acDefinitionProvider, + mock(AcDefinitionScanner.class), stageScanner, simpleScanner, phaseScanner, messageProvider); + var supervisionScanner = new SupervisionScanner(automationCompositionProvider, acDefinitionProvider, + messageProvider, monitoringScanner); + + supervisionScanner.run(); + verify(stageScanner, times(0)).scanStage(any(), any(), any(), any()); + verify(simpleScanner, times(0)).simpleScan(any(), any()); + verify(phaseScanner, times(0)).scanWithPhase(any(), any(), any()); + verify(messageProvider, times(0)).removeMessage(message.getMessageId()); + verify(messageProvider, times(0)).removeJob(JOB_ID); + } + @Test void testScanner() { var automationComposition = new AutomationComposition(); -- 2.16.6