Insert pending record when operation starts 05/108105/2
authorJim Hahn <jrh3@att.com>
Thu, 21 May 2020 21:04:17 +0000 (17:04 -0400)
committerJim Hahn <jrh3@att.com>
Thu, 21 May 2020 21:15:44 +0000 (17:15 -0400)
Modified code to insert a pending record when an operation starts.
Also modified it to update the existing record when the operation
completes, rather than adding a new record.
Note: the "outcome" for a "pending" record is left unset (i.e., it
is null).

Issue-ID: POLICY-2581
Change-Id: Ia1a02ed5a16b8af1328a49b22478fd57c4b9aca0
Signed-off-by: Jim Hahn <jrh3@att.com>
controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager2.java
controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/ophistory/OperationHistoryDataManagerImpl.java
controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager2Test.java
controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/ophistory/OperationHistoryDataManagerImplTest.java

index 9d7ca58..5e8cbbc 100644 (file)
@@ -500,20 +500,20 @@ public class ControlLoopOperationManager2 implements Serializable {
                     // operation started
                     ++attempts;
                     state = State.OPERATION_STARTED;
-                    operationHistory.add(new Operation(outcome));
-                    break;
-                }
 
-                /*
-                 * Operation completed. If the last entry was a "start" (i.e., "end" field
-                 * is null), then replace it. Otherwise, just add the completion.
-                 */
-                state = (outcome.getResult() == PolicyResult.SUCCESS ? State.OPERATION_SUCCESS
-                                : State.OPERATION_FAILURE);
-                controlLoopResponse = outcome.getControlLoopResponse();
-                if (!operationHistory.isEmpty() && operationHistory.peekLast().getClOperation().getEnd() == null) {
-                    operationHistory.removeLast();
+                } else {
+                    /*
+                     * Operation completed. If the last entry was a "start" (i.e., "end" field
+                     * is null), then replace it. Otherwise, just add the completion.
+                     */
+                    state = (outcome.getResult() == PolicyResult.SUCCESS ? State.OPERATION_SUCCESS
+                                    : State.OPERATION_FAILURE);
+                    controlLoopResponse = outcome.getControlLoopResponse();
+                    if (!operationHistory.isEmpty() && operationHistory.peekLast().getClOperation().getEnd() == null) {
+                        operationHistory.removeLast();
+                    }
                 }
+
                 operationHistory.add(new Operation(outcome));
                 storeOperationInDataBase();
                 break;
index 5de38a4..1d108a3 100644 (file)
@@ -21,6 +21,7 @@
 package org.onap.policy.controlloop.ophistory;
 
 import java.util.Date;
+import java.util.List;
 import java.util.Properties;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -78,10 +79,25 @@ public class OperationHistoryDataManagerImpl implements OperationHistoryDataMana
     private final BlockingQueue<Record> operations = new LinkedBlockingQueue<>();
 
     /**
-     * Number of records that have been added to the DB by this data manager instance.
+     * Number of records that have been processed and committed into the DB by this data
+     * manager instance.
      */
     @Getter
-    private long recordsAdded = 0;
+    private long recordsCommitted = 0;
+
+    /**
+     * Number of records that have been inserted into the DB by this data manager
+     * instance, whether or not they were committed.
+     */
+    @Getter
+    private long recordsInserted = 0;
+
+    /**
+     * Number of records that have been updated within the DB by this data manager
+     * instance, whether or not they were committed.
+     */
+    @Getter
+    private long recordsUpdated = 0;
 
 
     /**
@@ -228,7 +244,7 @@ public class OperationHistoryDataManagerImpl implements OperationHistoryDataMana
             }
 
             trans.commit();
-            recordsAdded += nrecords;
+            recordsCommitted += nrecords;
         }
     }
 
@@ -239,29 +255,59 @@ public class OperationHistoryDataManagerImpl implements OperationHistoryDataMana
      * @param record record to be stored
      */
     private void storeRecord(EntityManager entityMgr, Record record) {
-        Dbao newEntry = new Dbao();
 
         final VirtualControlLoopEvent event = record.getEvent();
         final ControlLoopOperation operation = record.getOperation();
 
         logger.info("store operation history record for {}", event.getRequestId());
 
-        newEntry.setClosedLoopName(event.getClosedLoopControlName());
-        newEntry.setRequestId(record.getRequestId());
-        newEntry.setActor(operation.getActor());
-        newEntry.setOperation(operation.getOperation());
-        newEntry.setTarget(record.getTargetEntity());
-        newEntry.setSubrequestId(operation.getSubRequestId());
-        newEntry.setMessage(operation.getMessage());
-        newEntry.setOutcome(operation.getOutcome());
+        List<Dbao> results =
+            entityMgr.createQuery("select e from Dbao e"
+                        + " where e.closedLoopName= ?1"
+                        + " and e.requestId= ?2"
+                        + " and e.subrequestId= ?3"
+                        + " and e.actor= ?4"
+                        + " and e.operation= ?5"
+                        + " and e.target= ?6",
+                        Dbao.class)
+                .setParameter(1, event.getClosedLoopControlName())
+                .setParameter(2, record.getRequestId())
+                .setParameter(3, operation.getSubRequestId())
+                .setParameter(4, operation.getActor())
+                .setParameter(5, operation.getOperation())
+                .setParameter(6, record.getTargetEntity())
+                .getResultList();
+
+        Dbao entry = (results.isEmpty() ? new Dbao() : results.get(0));
+
+        entry.setClosedLoopName(event.getClosedLoopControlName());
+        entry.setRequestId(record.getRequestId());
+        entry.setActor(operation.getActor());
+        entry.setOperation(operation.getOperation());
+        entry.setTarget(record.getTargetEntity());
+        entry.setSubrequestId(operation.getSubRequestId());
+        entry.setMessage(operation.getMessage());
+        entry.setOutcome(operation.getOutcome());
         if (operation.getStart() != null) {
-            newEntry.setStarttime(new Date(operation.getStart().toEpochMilli()));
+            entry.setStarttime(new Date(operation.getStart().toEpochMilli()));
+        } else {
+            entry.setStarttime(null);
         }
         if (operation.getEnd() != null) {
-            newEntry.setEndtime(new Date(operation.getEnd().toEpochMilli()));
+            entry.setEndtime(new Date(operation.getEnd().toEpochMilli()));
+        } else {
+            entry.setEndtime(null);
         }
 
-        entityMgr.persist(newEntry);
+        if (results.isEmpty()) {
+            logger.info("insert operation history record for {}", event.getRequestId());
+            ++recordsInserted;
+            entityMgr.persist(entry);
+        } else {
+            logger.info("update operation history record for {}", event.getRequestId());
+            ++recordsUpdated;
+            entityMgr.merge(entry);
+        }
     }
 
     /**
index 9c2e22d..3214add 100644 (file)
@@ -614,7 +614,7 @@ public class ControlLoopOperationManager2Test {
         assertFalse(mgr.nextStep());
         verify(mgrctx, times(4)).updated(mgr);
 
-        verifyDb(1, PolicyResult.SUCCESS, null);
+        verifyDb(2, PolicyResult.SUCCESS, null);
     }
 
     /**
@@ -640,7 +640,7 @@ public class ControlLoopOperationManager2Test {
 
         assertTrue(mgr.nextStep());
         assertEquals(ControlLoopOperationManager2.State.OPERATION_FAILURE, mgr.getState());
-        verifyDb(1, PolicyResult.FAILURE, null);
+        verifyDb(2, PolicyResult.FAILURE, null);
 
         assertThat(mgr.toString()).contains("attempts=1");
 
@@ -653,7 +653,7 @@ public class ControlLoopOperationManager2Test {
 
         assertTrue(mgr.nextStep());
         assertEquals(ControlLoopOperationManager2.State.OPERATION_FAILURE, mgr.getState());
-        verifyDb(2, PolicyResult.FAILURE, null);
+        verifyDb(4, PolicyResult.FAILURE, null);
 
         assertThat(mgr.toString()).contains("attempts=2");
 
@@ -665,7 +665,7 @@ public class ControlLoopOperationManager2Test {
 
         assertTrue(mgr.nextStep());
         assertEquals(ControlLoopOperationManager2.State.OPERATION_SUCCESS, mgr.getState());
-        verifyDb(3, PolicyResult.SUCCESS, null);
+        verifyDb(6, PolicyResult.SUCCESS, null);
 
         assertThat(mgr.toString()).contains("attempts=3");
 
index e6c66d1..e6d42d4 100644 (file)
@@ -53,6 +53,9 @@ import org.onap.policy.controlloop.ophistory.OperationHistoryDataManagerParams.O
 public class OperationHistoryDataManagerImplTest {
 
     private static final IllegalStateException EXPECTED_EXCEPTION = new IllegalStateException("expected exception");
+    private static final String MY_LOOP_NAME = "my-loop-name";
+    private static final String MY_ACTOR = "my-actor";
+    private static final String MY_OPERATION = "my-operation";
     private static final String MY_TARGET = "my-target";
     private static final String MY_ENTITY = "my-entity";
     private static final String REQ_ID = "my-request-id";
@@ -109,10 +112,14 @@ public class OperationHistoryDataManagerImplTest {
         MockitoAnnotations.initMocks(this);
 
         event = new VirtualControlLoopEvent();
+        event.setClosedLoopControlName(MY_LOOP_NAME);
         event.setRequestId(UUID.randomUUID());
 
         operation = new ControlLoopOperation();
+        operation.setActor(MY_ACTOR);
+        operation.setOperation(MY_OPERATION);
         operation.setTarget(MY_TARGET);
+        operation.setSubRequestId(UUID.randomUUID().toString());
 
         threadFunction = null;
         finished = new CountDownLatch(1);
@@ -170,7 +177,7 @@ public class OperationHistoryDataManagerImplTest {
 
         runThread();
 
-        assertEquals(1, mgr.getRecordsAdded());
+        assertEquals(1, mgr.getRecordsCommitted());
     }
 
     /**
@@ -195,7 +202,7 @@ public class OperationHistoryDataManagerImplTest {
         // store
         mgr.store(REQ_ID, event, MY_ENTITY, operation);
 
-        assertEquals(0, mgr.getRecordsAdded());
+        assertEquals(0, mgr.getRecordsCommitted());
     }
 
     /**
@@ -210,7 +217,7 @@ public class OperationHistoryDataManagerImplTest {
 
         runThread();
 
-        assertEquals(MAX_QUEUE_LENGTH, mgr.getRecordsAdded());
+        assertEquals(MAX_QUEUE_LENGTH, mgr.getRecordsCommitted());
     }
 
     @Test
@@ -234,7 +241,7 @@ public class OperationHistoryDataManagerImplTest {
 
         verify(emfSpy).close();
 
-        assertEquals(3, mgr.getRecordsAdded());
+        assertEquals(3, mgr.getRecordsCommitted());
     }
 
     private void waitForThread() {
@@ -286,28 +293,77 @@ public class OperationHistoryDataManagerImplTest {
 
     @Test
     public void testStoreRecord() throws InterruptedException {
+        /*
+         * Note: we change sub-request ID each time to guarantee that the records are
+         * unique.
+         */
+
         // no start time
         mgr.store(REQ_ID, event, MY_ENTITY, operation);
 
-        // no start time
+        // no end time
         operation = new ControlLoopOperation(operation);
+        operation.setSubRequestId(UUID.randomUUID().toString());
         operation.setStart(Instant.now());
         mgr.store(REQ_ID, event, MY_ENTITY, operation);
 
         // both start and end times
         operation = new ControlLoopOperation(operation);
+        operation.setSubRequestId(UUID.randomUUID().toString());
         operation.setEnd(Instant.now());
         mgr.store(REQ_ID, event, MY_ENTITY, operation);
 
         // only end time
         operation = new ControlLoopOperation(operation);
+        operation.setSubRequestId(UUID.randomUUID().toString());
+        operation.setStart(null);
+        mgr.store(REQ_ID, event, MY_ENTITY, operation);
+
+        runThread();
+
+        // all of them should have been stored
+        assertEquals(4, mgr.getRecordsCommitted());
+
+        // each was unique
+        assertEquals(4, mgr.getRecordsInserted());
+        assertEquals(0, mgr.getRecordsUpdated());
+    }
+
+    /**
+     * Tests storeRecord() when records are updated.
+     */
+    @Test
+    public void testStoreRecordUpdate() throws InterruptedException {
+        /*
+         * Note: we do NOT change sub-request ID, so that records all refer to the same DB
+         * record.
+         */
+
+        // no start time
+        mgr.store(REQ_ID, event, MY_ENTITY, operation);
+
+        // no end time
+        operation.setStart(Instant.now());
+        mgr.store(REQ_ID, event, MY_ENTITY, operation);
+
+        // both start and end times
+        operation.setEnd(Instant.now());
+        mgr.store(REQ_ID, event, MY_ENTITY, operation);
+
+        // only end time
         operation.setStart(null);
         mgr.store(REQ_ID, event, MY_ENTITY, operation);
 
         runThread();
 
         // all of them should have been stored
-        assertEquals(4, mgr.getRecordsAdded());
+        assertEquals(4, mgr.getRecordsCommitted());
+
+        // only one new record
+        assertEquals(1, mgr.getRecordsInserted());
+
+        // remainder were updates
+        assertEquals(3, mgr.getRecordsUpdated());
     }
 
     private void runThread() throws InterruptedException {