Fixes in DataGrooming
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / dbgen / DataGrooming.java
index afc7807..61ef854 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * org.onap.aai
  * ================================================================================
- * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 AT&T Intellectual Property. 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.
@@ -16,8 +16,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * ============LICENSE_END=========================================================
- *
- * ECOMP is a trademark and service mark of AT&T Intellectual Property.
  */
 package org.onap.aai.dbgen;
 
@@ -50,6 +48,7 @@ import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 import org.onap.aai.db.props.AAIProperties;
 import org.onap.aai.dbmap.AAIGraph;
+import org.onap.aai.dbmap.AAIGraphConfig;
 import org.onap.aai.exceptions.AAIException;
 import org.onap.aai.introspection.Introspector;
 import org.onap.aai.introspection.Loader;
@@ -57,25 +56,28 @@ import org.onap.aai.introspection.LoaderFactory;
 import org.onap.aai.introspection.ModelType;
 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
 import org.onap.aai.logging.ErrorLogHelper;
+import org.onap.aai.logging.LogFormatTools;
+import org.onap.aai.logging.LoggingContext;
 import org.onap.aai.serialization.db.AAIDirection;
 import org.onap.aai.serialization.db.EdgeProperty;
-import org.onap.aai.util.AAIConfig;
-import org.onap.aai.util.AAIConstants;
-import org.onap.aai.util.FormatDate;
+import org.onap.aai.util.*;
+import org.onap.aai.logging.LoggingContext.StatusCode;
 
 import com.att.eelf.configuration.Configuration;
 import com.att.eelf.configuration.EELFLogger;
 import com.att.eelf.configuration.EELFManager;
-import com.thinkaurelius.titan.core.TitanFactory;
-import com.thinkaurelius.titan.core.TitanGraph;
+import org.janusgraph.core.JanusGraphFactory;
+import org.janusgraph.core.JanusGraph;
 
 
 public class DataGrooming {
 
-       private static EELFLogger LOGGER;
+       private static EELFLogger logger;
        private static final String FROMAPPID = "AAI-DB";
        private static final String TRANSID = UUID.randomUUID().toString();
        private static int dupeGrpsDeleted = 0;
+       private static final String AAI_NODE_TYPE = "aai-node-type";
+       private static final String KEEP_VID_UNDETERMINED ="KeepVid=UNDETERMINED";
        
        /**
         * The main method.
@@ -83,12 +85,13 @@ public class DataGrooming {
         * @param args the arguments
         */
        public static void main(String[] args) {
-               
+
                // Set the logging file properties to be used by EELFManager
+               System.setProperty("aai.service.name", DataGrooming.class.getSimpleName());
                Properties props = System.getProperties();
                props.setProperty(Configuration.PROPERTY_LOGGING_FILE_NAME, AAIConstants.AAI_DATA_GROOMING_LOGBACK_PROPS);
                props.setProperty(Configuration.PROPERTY_LOGGING_FILE_PATH, AAIConstants.AAI_HOME_ETC_APP_PROPERTIES);
-               LOGGER = EELFManager.getInstance().getLogger(DataGrooming.class);
+               logger = EELFManager.getInstance().getLogger(DataGrooming.class);
                String ver = "version"; // Placeholder
                Boolean doAutoFix = false;
                Boolean edgesOnlyFlag = false;
@@ -102,145 +105,169 @@ public class DataGrooming {
                Boolean neverUseCache = false;
                Boolean skipEdgeCheckFlag = false;
                
+               LoggingContext.init();
+               LoggingContext.partnerName(FROMAPPID);
+               LoggingContext.serviceName(AAIConstants.AAI_RESOURCES_MS);
+               LoggingContext.component("dataGrooming");
+               LoggingContext.targetEntity(AAIConstants.AAI_RESOURCES_MS);
+               LoggingContext.targetServiceName("main");
+               LoggingContext.requestId(TRANSID);
+               LoggingContext.statusCode(StatusCode.COMPLETE);
+               LoggingContext.responseCode(LoggingContext.SUCCESS);
+
                int timeWindowMinutes = 0; // A value of 0 means that we will not have a time-window -- we will look
                                    // at all nodes of the passed-in nodeType. 
-               long windowStartTime = 0; // Translation of the window into a starting timestamp 
+               
                
                int maxRecordsToFix = AAIConstants.AAI_GROOMING_DEFAULT_MAX_FIX;
                int sleepMinutes = AAIConstants.AAI_GROOMING_DEFAULT_SLEEP_MINUTES;
                try {
                        String maxFixStr = AAIConfig.get("aai.grooming.default.max.fix");
-                       if( maxFixStr != null &&  !maxFixStr.equals("") ){
+                       if( maxFixStr != null &&  !maxFixStr.isEmpty() ){
                                maxRecordsToFix = Integer.parseInt(maxFixStr);
                        }
                        String sleepStr = AAIConfig.get("aai.grooming.default.sleep.minutes");
-                       if( sleepStr != null &&  !sleepStr.equals("") ){
+                       if( sleepStr != null &&  !sleepStr.isEmpty() ){
                                sleepMinutes = Integer.parseInt(sleepStr);
                        }
                }
                catch ( Exception e ){
                        // Don't worry, we'll just use the defaults that we got from AAIConstants
-                       LOGGER.warn("WARNING - could not pick up aai.grooming values from aaiconfig.properties file. ");
+                       logger.warn("WARNING - could not pick up aai.grooming values from aaiconfig.properties file. ");
                }
                
                String prevFileName = "";
                dupeGrpsDeleted = 0;
                FormatDate fd = new FormatDate("yyyyMMddHHmm", "GMT");
                String dteStr = fd.getDateTime();
-               String groomOutFileName = "dataGrooming." + dteStr + ".out";
 
-               String argString = "";
-               for( int x = 0; x < args.length; x++ ) {
-                       argString = argString + " " + args[x];
-               }
-               LOGGER.info(" DataGrooming called with these options: [" + argString + "]");
-               
                if (args.length > 0) {
                        // They passed some arguments in that will affect processing
-                       
                        for (int i = 0; i < args.length; i++) {
                                String thisArg = args[i];
-                               if (thisArg.equals("-edgesOnly")) {
+                               if ("-edgesOnly".equals(thisArg)) {
                                        edgesOnlyFlag = true;
                                } else if (thisArg.equals("-autoFix")) {
                                        doAutoFix = true;
-                               } else if (thisArg.equals("-skipHostCheck")) {
+                               } else if ("-skipHostCheck".equals(thisArg)) {
                                        skipHostCheck = true;
-                               } else if (thisArg.equals("-dontFixOrphans")) {
+                               } else if ("-dontFixOrphans".equals(thisArg)) {
                                        dontFixOrphansFlag = true;
-                               } else if (thisArg.equals("-singleCommits")) {
+                               } else if ("-singleCommits".equals(thisArg)) {
                                        singleCommits = true;
-                               } else if (thisArg.equals("-dupeCheckOff")) {
+                               } else if ("-dupeCheckOff".equals(thisArg)) {
                                        dupeCheckOff = true;
-                               } else if (thisArg.equals("-dupeFixOn")) {
+                               } else if ("-dupeFixOn".equals(thisArg)) {
                                        dupeFixOn = true;
-                               } else if (thisArg.equals("-ghost2CheckOff")) {
+                               } else if ("-ghost2CheckOff".equals(thisArg)) {
                                        ghost2CheckOff = true;
-                               } else if (thisArg.equals("-neverUseCache")) {
+                               } else if ("-neverUseCache".equals(thisArg)) {
                                        neverUseCache = true;
-                               } else if (thisArg.equals("-ghost2FixOn")) {
+                               } else if ("-ghost2FixOn".equals(thisArg)) {
                                        ghost2FixOn = true;
-                               } else if (thisArg.equals("-skipEdgeChecks")) {
+                               } else if ("-skipEdgeChecks".equals(thisArg)) {
                                        skipEdgeCheckFlag = true;
-                               } else if (thisArg.equals("-maxFix")) {
+                               } else if ("-maxFix".equals(thisArg)) {
                                        i++;
                                        if (i >= args.length) {
-                                               LOGGER.error(" No value passed with -maxFix option.  ");
-                                               System.exit(0);
+                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                               LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR);
+                                               logger.error(" No value passed with -maxFix option.  ");
+                                               AAISystemExitUtil.systemExitCloseAAIGraph(0);
                                        }
                                        String nextArg = args[i];
                                        try {
                                                maxRecordsToFix = Integer.parseInt(nextArg);
                                        } catch (Exception e) {
-                                               LOGGER.error("Bad value passed with -maxFix option: ["
+                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                               LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR);
+                                               logger.error("Bad value passed with -maxFix option: ["
                                                                                + nextArg + "]");
-                                               System.exit(0);
+                                               AAISystemExitUtil.systemExitCloseAAIGraph(0);
                                        }
                                } else if (thisArg.equals("-sleepMinutes")) {
                                        i++;
                                        if (i >= args.length) {
-                                               LOGGER.error("No value passed with -sleepMinutes option.");
-                                               System.exit(0);
+                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                               LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR);
+                                               logger.error("No value passed with -sleepMinutes option.");
+                                               AAISystemExitUtil.systemExitCloseAAIGraph(0);
                                        }
                                        String nextArg = args[i];
                                        try {
                                                sleepMinutes = Integer.parseInt(nextArg);
                                        } catch (Exception e) {
-                                               LOGGER.error("Bad value passed with -sleepMinutes option: ["
+                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                               LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR);
+                                               logger.error("Bad value passed with -sleepMinutes option: ["
                                                                                + nextArg + "]");
-                                               System.exit(0);
+                                               AAISystemExitUtil.systemExitCloseAAIGraph(0);
                                        }
-                               } else if (thisArg.equals("-timeWindowMinutes")) {
+                               } else if ("-timeWindowMinutes".equals(thisArg)) {
                                        i++;
                                        if (i >= args.length) {
-                                               LOGGER.error("No value passed with -timeWindowMinutes option.");
-                                               System.exit(0);
+                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                               LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR);
+                                               logger.error("No value passed with -timeWindowMinutes option.");
+                                               AAISystemExitUtil.systemExitCloseAAIGraph(0);
                                        }
                                        String nextArg = args[i];
                                        try {
                                                timeWindowMinutes = Integer.parseInt(nextArg);
                                        } catch (Exception e) {
-                                               LOGGER.error("Bad value passed with -timeWindowMinutes option: ["
+                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                               LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR);
+                                               logger.error("Bad value passed with -timeWindowMinutes option: ["
                                                                                + nextArg + "]");
-                                               System.exit(0);
+                                               AAISystemExitUtil.systemExitCloseAAIGraph(0);
                                        }
-                                       if( timeWindowMinutes > 0 ){
-                                               // Translate the window value (ie. 30 minutes) into a unix timestamp like
-                                               //    we use in the db - so we can select data created after that time.
-                                               windowStartTime = figureWindowStartTime( timeWindowMinutes );
-                                       }
-                               } else if (thisArg.equals("-f")) {
+                                       
+                               } else if ("-f".equals(thisArg)) {
                                        i++;
                                        if (i >= args.length) {
-                                               LOGGER.error(" No value passed with -f option. ");
-                                               System.exit(0);
+                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                               LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR);
+                                               logger.error(" No value passed with -f option. ");
+                                               AAISystemExitUtil.systemExitCloseAAIGraph(0);
                                        }
                                        prevFileName = args[i];
                                } else {
-                                       LOGGER.error(" Unrecognized argument passed to DataGrooming: ["
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR);
+                                       logger.error(" Unrecognized argument passed to DataGrooming: ["
                                                                        + thisArg + "]. ");
-                                       LOGGER.error(" Valid values are: -f -autoFix -maxFix -edgesOnly -dupeFixOn -donFixOrphans -sleepMinutes -neverUseCache");
-                                       System.exit(0);
+                                       logger.error(" Valid values are: -f -autoFix -maxFix -edgesOnly -skipEdgeChecks -dupeFixOn -donFixOrphans -timeWindowMinutes -sleepMinutes -neverUseCache");
+                                       AAISystemExitUtil.systemExitCloseAAIGraph(0);
                                }
                        }
                }
                
-
+               String windowTag = "FULL";
+               if( timeWindowMinutes > 0 ){
+                       windowTag = "PARTIAL";
+               }
+               String groomOutFileName = "dataGrooming." + windowTag + "." + dteStr + ".out";
+               
                try {
                        LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
 
                }
                catch (Exception ex){
-                       LOGGER.error("ERROR - Could not create loader", ex);
-                       System.exit(1);
+                       LoggingContext.statusCode(StatusCode.ERROR);
+                       LoggingContext.responseCode(LoggingContext.BUSINESS_PROCESS_ERROR);
+                       logger.error("ERROR - Could not create loader " + LogFormatTools.getStackTop(ex));
+                       AAISystemExitUtil.systemExitCloseAAIGraph(1);
                }
 
+               if (skipHostCheck) {
+                       logger.info(" We will skip the HostCheck as requested. ");
+               }
 
                try {
-                       if (!prevFileName.equals("")) {
+                       if (!prevFileName.isEmpty()) {
                                // They are trying to fix some data based on a data in a
                                // previous file.
-                               LOGGER.info(" Call doTheGrooming() with a previous fileName ["
+                               logger.info(" Call doTheGrooming() with a previous fileName ["
                                                                + prevFileName + "] for cleanup. ");
                                Boolean finalShutdownFlag = true;
                                Boolean cacheDbOkFlag = false;
@@ -248,7 +275,7 @@ public class DataGrooming {
                                                maxRecordsToFix, groomOutFileName, ver, singleCommits,
                                                dupeCheckOff, dupeFixOn, ghost2CheckOff, ghost2FixOn, 
                                                finalShutdownFlag, cacheDbOkFlag, 
-                                               skipEdgeCheckFlag, windowStartTime);
+                                               skipEdgeCheckFlag, timeWindowMinutes);
                        } else if (doAutoFix) {
                                // They want us to run the processing twice -- first to look for
                                // delete candidates, then after
@@ -256,34 +283,34 @@ public class DataGrooming {
                                // that were found by the first run.
                                // Note: we will produce a separate output file for each of the
                                // two runs.
-                               LOGGER.info(" Doing an auto-fix call to Grooming. ");
-                               LOGGER.info(" First, Call doTheGrooming() to look at what's out there. ");
+                               logger.info(" Doing an auto-fix call to Grooming. ");
+                               logger.info(" First, Call doTheGrooming() to look at what's out there. ");
                                Boolean finalShutdownFlag = false;
                                Boolean cacheDbOkFlag = true;
                                int fixCandCount = doTheGrooming("", edgesOnlyFlag,
                                                dontFixOrphansFlag, maxRecordsToFix, groomOutFileName,
                                                ver, singleCommits, dupeCheckOff, dupeFixOn, ghost2CheckOff, ghost2FixOn, 
                                                finalShutdownFlag, cacheDbOkFlag, 
-                                               skipEdgeCheckFlag, windowStartTime);
+                                               skipEdgeCheckFlag, timeWindowMinutes);
                                if (fixCandCount == 0) {
-                                       LOGGER.info(" No fix-Candidates were found by the first pass, so no second/fix-pass is needed. ");
+                                       logger.info(" No fix-Candidates were found by the first pass, so no second/fix-pass is needed. ");
                                } else {
                                        // We'll sleep a little and then run a fix-pass based on the
                                        // first-run's output file.
                                        try {
-                                               LOGGER.info("About to sleep for " + sleepMinutes
+                                               logger.info("About to sleep for " + sleepMinutes
                                                                + " minutes.");
                                                int sleepMsec = sleepMinutes * 60 * 1000;
                                                Thread.sleep(sleepMsec);
                                        } catch (InterruptedException ie) {
-                                               LOGGER.info("\n >>> Sleep Thread has been Interrupted <<< ");
-                                               System.exit(0);
+                                               logger.info("\n >>> Sleep Thread has been Interrupted <<< ");
+                                               AAISystemExitUtil.systemExitCloseAAIGraph(0);
                                        }
 
                                        dteStr = fd.getDateTime();
                                        String secondGroomOutFileName = "dataGrooming." + dteStr
                                                        + ".out";
-                                       LOGGER.info(" Now, call doTheGrooming() a second time and pass in the name of the file "
+                                       logger.info(" Now, call doTheGrooming() a second time and pass in the name of the file "
                                                                        + "generated by the first pass for fixing: ["
                                                                        + groomOutFileName + "]");
                                        finalShutdownFlag = true;
@@ -293,13 +320,13 @@ public class DataGrooming {
                                                        secondGroomOutFileName, ver, singleCommits,
                                                        dupeCheckOff, dupeFixOn, ghost2CheckOff, ghost2FixOn, 
                                                        finalShutdownFlag, cacheDbOkFlag, 
-                                                       skipEdgeCheckFlag, windowStartTime);
+                                                       skipEdgeCheckFlag, timeWindowMinutes);
                                }
                        } else {
                                // Do the grooming - plain vanilla (no fix-it-file, no
                                // auto-fixing)
                                Boolean finalShutdownFlag = true;
-                               LOGGER.info(" Call doTheGrooming() ");
+                               logger.info(" Call doTheGrooming() ");
                                Boolean cacheDbOkFlag = true;
                                if( neverUseCache ){
                                        // They have forbidden us from using a cached db connection.
@@ -309,14 +336,16 @@ public class DataGrooming {
                                                maxRecordsToFix, groomOutFileName, ver, singleCommits,
                                                dupeCheckOff, dupeFixOn, ghost2CheckOff, ghost2FixOn, 
                                                finalShutdownFlag, cacheDbOkFlag, 
-                                               skipEdgeCheckFlag, windowStartTime);
+                                               skipEdgeCheckFlag, timeWindowMinutes);
                        }
                } catch (Exception ex) {
-                       LOGGER.error("Exception while grooming data", ex);
+                       LoggingContext.statusCode(StatusCode.ERROR);
+                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                       logger.error("Exception while grooming data " + LogFormatTools.getStackTop(ex));
                }
 
-               LOGGER.info(" Done! ");
-               System.exit(0);
+               logger.info(" Done! ");
+               AAISystemExitUtil.systemExitCloseAAIGraph(0);
 
        }// End of main()
 
@@ -345,14 +374,15 @@ public class DataGrooming {
                        Boolean dupeCheckOff, Boolean dupeFixOn,
                        Boolean ghost2CheckOff, Boolean ghost2FixOn, 
                        Boolean finalShutdownFlag, Boolean cacheDbOkFlag,
-                       Boolean skipEdgeCheckFlag, long windowStartTime) {
+                       Boolean skipEdgeCheckFlag, int timeWindowMinutes) {
 
-               LOGGER.debug(" Entering doTheGrooming \n");
+               logger.debug(" Entering doTheGrooming \n");
 
                int cleanupCandidateCount = 0;
+               long windowStartTime = 0; // Translation of the window into a starting timestamp 
                BufferedWriter bw = null;
-               TitanGraph graph = null;
-               TitanGraph graph2 = null;
+               JanusGraph graph = null;
+               JanusGraph graph2 = null;
                int deleteCount = 0;
                boolean executeFinalCommit = false;
                Set<String> deleteCandidateList = new LinkedHashSet<>();
@@ -360,6 +390,12 @@ public class DataGrooming {
                Graph g = null;
                Graph g2 = null;
                try {
+                       if( timeWindowMinutes > 0 ){
+                               // Translate the window value (ie. 30 minutes) into a unix timestamp like
+                               //    we use in the db - so we can select data created after that time.
+                               windowStartTime = figureWindowStartTime( timeWindowMinutes );
+                       }
+                       
                        AAIConfig.init();
                        String targetDir = AAIConstants.AAI_HOME + AAIConstants.AAI_FILESEP
                                        + "logs" + AAIConstants.AAI_FILESEP + "data"
@@ -368,14 +404,16 @@ public class DataGrooming {
                        // Make sure the target directory exists
                        new File(targetDir).mkdirs();
 
-                       if (!fileNameForFixing.equals("")) {
+                       if (!fileNameForFixing.isEmpty()) {
                                deleteCandidateList = getDeleteList(targetDir,
                                                fileNameForFixing, edgesOnlyFlag, dontFixOrphansFlag,
                                                dupeFixOn);
                        }
 
                        if (deleteCandidateList.size() > maxRecordsToFix) {
-                               LOGGER.warn(" >> WARNING >>  Delete candidate list size ("
+                               LoggingContext.statusCode(StatusCode.ERROR);
+                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                               logger.warn(" >> WARNING >>  Delete candidate list size ("
                                                + deleteCandidateList.size()
                                                + ") is too big.  The maxFix we are using is: "
                                                + maxRecordsToFix
@@ -395,26 +433,26 @@ public class DataGrooming {
                                throw new AAIException("AAI_6124", emsg);
                        }
 
-                       LOGGER.info(" Will write to " + fullOutputFileName );
+                       logger.info(" Will write to " + fullOutputFileName );
                        bw = new BufferedWriter(new FileWriter(groomOutFile.getAbsoluteFile()));
                        ErrorLogHelper.loadProperties();
                        
-                       LOGGER.info("    ---- NOTE --- about to open graph (takes a little while)--------\n");
+                       logger.info("    ---- NOTE --- about to open graph (takes a little while)--------\n");
 
                        if( cacheDbOkFlag ){
                                // Since we're just reading (not deleting/fixing anything), we can use 
                                // a cached connection to the DB
-                               graph = TitanFactory.open(AAIConstants.CACHED_DB_CONFIG);
+                               graph = JanusGraphFactory.open(new AAIGraphConfig.Builder(AAIConstants.CACHED_DB_CONFIG).forService(DataGrooming.class.getSimpleName()).withGraphType("cached").buildConfiguration());
                        }
                        else {
-                               graph = TitanFactory.open(AAIConstants.REALTIME_DB_CONFIG);
+                               graph = JanusGraphFactory.open(new AAIGraphConfig.Builder(AAIConstants.REALTIME_DB_CONFIG).forService(DataGrooming.class.getSimpleName()).withGraphType("realtime1").buildConfiguration());
                        }
                        if (graph == null) {
                                String emsg = "null graph object in DataGrooming\n";
                                throw new AAIException("AAI_6101", emsg);
                        }
                
-                       LOGGER.debug(" Got the graph object. ");
+                       logger.debug(" Got the graph object. ");
                        
                        g = graph.newTransaction();
                        if (g == null) {
@@ -438,22 +476,26 @@ public class DataGrooming {
                        Set<Entry<String, Introspector>> entrySet = loader.getAllObjects().entrySet();
                        String ntList = "";
 
-                       LOGGER.info("  Starting DataGrooming Processing ");
+                       logger.info("  Starting DataGrooming Processing ");
 
                        if (edgesOnlyFlag) {
-                               LOGGER.info(" NOTE >> Skipping Node processing as requested.  Will only process Edges. << ");
+                               logger.info(" NOTE >> Skipping Node processing as requested.  Will only process Edges. << ");
                        } 
                        else {
                                for (Entry<String, Introspector> entry : entrySet) {
                                        String nType = entry.getKey();
                                        int thisNtCount = 0;
                                        int thisNtDeleteCount = 0;
-                                       LOGGER.debug(" >  Look at : [" + nType + "] ...");
+                                       
+                                       logger.debug(" >  Look at : [" + nType + "] ...");
                                        ntList = ntList + "," + nType;
 
                                        // Get a collection of the names of the key properties for this nodeType to use later
-                                       // Determine what the key fields are for this nodeType
-                                       Collection <String> keyProps = entry.getValue().getKeys();
+                                       // Determine what the key fields are for this nodeType - use an arrayList so they
+                                       // can be gotten out in a consistent order.
+                                       Set <String> keyPropsSet = entry.getValue().getKeys();
+                                       ArrayList <String> keyProps = new ArrayList <String> ();
+                                       keyProps.addAll(keyPropsSet);
                                        
                                        // Get the types of nodes that this nodetype depends on for uniqueness (if any)
                                        Collection <String> depNodeTypes = loader.introspectorFromName(nType).getDependentOn();
@@ -461,7 +503,7 @@ public class DataGrooming {
                                        // Loop through all the nodes of this Node type
                                        int lastShownForNt = 0;
                                        ArrayList <Vertex> tmpList = new ArrayList <> ();
-                                       Iterator <Vertex> iterv =  source1.V().has("aai-node-type",nType); 
+                                       Iterator <Vertex> iterv =  source1.V().has(AAI_NODE_TYPE,nType);
                                        while (iterv.hasNext()) {
                                                // We put the nodes into an ArrayList because the graph.query iterator can time out
                                                tmpList.add(iterv.next());
@@ -473,16 +515,17 @@ public class DataGrooming {
                                                        thisNtCount++;
                                                        if( thisNtCount == lastShownForNt + 250 ){
                                                                lastShownForNt = thisNtCount;
-                                                               LOGGER.debug("count for " + nType + " so far = " + thisNtCount );
+                                                               logger.debug("count for " + nType + " so far = " + thisNtCount );
                                                        }
                                                        Vertex thisVtx = iter.next();
                                                        if( windowStartTime > 0 ){
-                                                               // We only want nodes that are created after a passed-in timestamp
-                                                               Object objTimeStamp = thisVtx.property("aai-created-ts").orElse(null);
-                                                               if( objTimeStamp != null ){
-                                                                       long thisNodeCreateTime = (long)objTimeStamp;
-                                                                       if( thisNodeCreateTime < windowStartTime ){
-                                                                               // It is NOT in our window, so we can pass over it
+                                                               // They are using the time-window, so we only want nodes that are updated after a
+                                                               // passed-in timestamp OR that have no last-modified-timestamp which means they are suspicious.
+                                                               Object objModTimeStamp = thisVtx.property("aai-last-mod-ts").orElse(null);
+                                                               if( objModTimeStamp != null ){
+                                                                       long thisNodeModTime = (long)objModTimeStamp;
+                                                                       if( thisNodeModTime < windowStartTime ){
+                                                                               // It has a last modified ts and is NOT in our window, so we can pass over it
                                                                                continue;
                                                                        }
                                                                }
@@ -490,7 +533,7 @@ public class DataGrooming {
                                                        
                                                        String thisVid = thisVtx.id().toString();
                                                        if (processedVertices.contains(thisVid)) {
-                                                               LOGGER.debug("skipping already processed vertex: " + thisVid);
+                                                               logger.debug("skipping already processed vertex: " + thisVid);
                                                                continue;
                                                        }
                                                        totalNodeCount++;
@@ -521,7 +564,9 @@ public class DataGrooming {
                                                                boolean depNodeOk = true;
                                                                if( depNodeTypes.isEmpty() ){
                                                                        // This kind of node is not dependent on any other.
-                                                                       // Make sure we can get it back using it's key properties and that we only get one.
+                                                                       // Make sure we can get it back using it's key properties (that is the
+                                                                       //   phantom checking) and that we only get one.  Note - we also need
+                                                                       //   to collect data for a second type of dupe-checking which is done later.
                                                                        secondGetList = getNodeJustUsingKeyParams( TRANSID, FROMAPPID, source1, nType, 
                                                                                        propHashWithKeys, version );
                                                                } 
@@ -537,10 +582,6 @@ public class DataGrooming {
                                                                                pCount++;
                                                                        }
                                                                        if( pCount <= 0 ){
-                                                                       
-                                                                       
-                                                                       //List<Vertex> vertI2 = g.traversal().V(thisVtx).union(__.outE().has("isParent-REV",true).outV(),__.inE().has("isParent",true).inV()).toList();
-                                                                       //if( vertI2.isEmpty()){
                                                                                        
                                                                                // It's Missing it's dependent/parent node 
                                                                                depNodeOk = false;
@@ -556,7 +597,9 @@ public class DataGrooming {
                                                                                                zeroEdges = true;
                                                                                        }
                                                                                } catch (Exception ex) {
-                                                                                       LOGGER.warn("WARNING from inside the for-each-vid-loop orphan-edges-check ", ex);
+                                                                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                                                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                                       logger.warn("WARNING from inside the for-each-vid-loop orphan-edges-check " + LogFormatTools.getStackTop(ex) );
                                                                                }
                                                                                
                                                                                if (deleteCandidateList.contains(thisVid)) {
@@ -568,10 +611,12 @@ public class DataGrooming {
                                                                                                thisNtDeleteCount++;
                                                                                        } catch (Exception e) {
                                                                                                okFlag = false;
-                                                                                               LOGGER.error("ERROR trying to delete missing-dep-node VID = " + thisVid, e);
+                                                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                                               logger.error("ERROR trying to delete missing-dep-node VID = " + thisVid + " " + LogFormatTools.getStackTop(e));
                                                                                        }
                                                                                        if (okFlag) {
-                                                                                               LOGGER.info(" DELETED missing-dep-node VID = " + thisVid);
+                                                                                               logger.info(" DELETED missing-dep-node VID = " + thisVid);
                                                                                        }
                                                                                } else {
                                                                                        // We count nodes missing their depNodes two ways - the first if it has
@@ -605,9 +650,9 @@ public class DataGrooming {
                                                                                        }
                                                                                }
                                                                        }
-                                                               }
+                                                               }// end of -- else this is a dependent node  -- piece
                                                                
-                                                               if( depNodeOk && (secondGetList == null || secondGetList.size() == 0) ){
+                                                               if( depNodeOk && (secondGetList == null || secondGetList.isEmpty()) ){
                                                                        // We could not get the node back using it's own key info. 
                                                                        // So, it's a PHANTOM
                                                                        if (deleteCandidateList.contains(thisVid)) {
@@ -618,10 +663,12 @@ public class DataGrooming {
                                                                                        thisNtDeleteCount++;
                                                                                } catch (Exception e) {
                                                                                        okFlag = false;
-                                                                                       LOGGER.error("ERROR trying to delete phantom VID = " + thisVid, e);
+                                                                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                                                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                                       logger.error("ERROR trying to delete phantom VID = " + thisVid + " " + LogFormatTools.getStackTop(e));
                                                                                }
                                                                                if (okFlag) {
-                                                                                       LOGGER.info(" DELETED VID = " + thisVid);
+                                                                                       logger.info(" DELETED VID = " + thisVid);
                                                                                }
                                                                        } else {
                                                                                ghostNodeHash.put(thisVid, thisVtx);
@@ -629,7 +676,7 @@ public class DataGrooming {
                                                                }
                                                                else if( (secondGetList.size() > 1) && depNodeOk && !dupeCheckOff ){
                                                                        // Found some DUPLICATES - need to process them
-                                                                       LOGGER.info(" - now check Dupes for this guy - ");
+                                                                       logger.info(" - now check Dupes for this guy - ");
                                                                        List<String> tmpDupeGroups = checkAndProcessDupes(
                                                                                                TRANSID, FROMAPPID, g, source1, version,
                                                                                                nType, secondGetList, dupeFixOn,
@@ -638,27 +685,62 @@ public class DataGrooming {
                                                                        while (dIter.hasNext()) {
                                                                                // Add in any newly found dupes to our running list
                                                                                String tmpGrp = dIter.next();
-                                                                               LOGGER.info("Found set of dupes: [" + tmpGrp + "]");
+                                                                               logger.info("Found set of dupes: [" + tmpGrp + "]");
                                                                                dupeGroups.add(tmpGrp);
                                                                        }
                                                                }
                                                        } 
                                                        catch (AAIException e1) {
-                                                               LOGGER.warn(" For nodeType = " + nType + " Caught exception", e1);
+                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                               logger.warn(" For nodeType = " + nType + " Caught exception", e1);
                                                                errArr.add(e1.getErrorObject().toString());
                                                        }
                                                        catch (Exception e2) {
-                                                               LOGGER.warn(" For nodeType = " + nType
+                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                               logger.warn(" For nodeType = " + nType
                                                                                + " Caught exception", e2);
                                                                errArr.add(e2.getMessage());
                                                        }
-                                               }// try block to enclose looping of a single vertex
+                                               }// try block to enclose looping over each single vertex
                                                catch (Exception exx) {
-                                                       LOGGER.warn("WARNING from inside the while-verts-loop ", exx);
+                                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                       logger.warn("WARNING from inside the while-verts-loop ", exx);
                                                }
                                                
                                        } // while loop for each record of a nodeType
                                        
+                                       if( depNodeTypes.isEmpty() && !dupeCheckOff ){
+                                               // For this nodeType, we haven't looked at the possibility of a 
+                                               // non-dependent node where two verts have same key info
+                                               ArrayList<ArrayList<Vertex>> nonDependentDupeSets = new ArrayList<ArrayList<Vertex>>();
+                                                       nonDependentDupeSets = getDupeSets4NonDepNodes( 
+                                                                               TRANSID, FROMAPPID, g,
+                                                                               version, nType, tmpList, 
+                                                                               keyProps, loader );
+                                               // For each set found (each set is for a unique instance of key-values),
+                                               //  process the dupes found
+                                               Iterator<ArrayList<Vertex>> dsItr = nonDependentDupeSets.iterator();
+                                               while( dsItr.hasNext() ){
+                                                       ArrayList<Vertex> dupeList =  dsItr.next();
+                                                       logger.info(" - now check Dupes for some non-dependent guys - ");
+                                                       List<String> tmpDupeGroups = checkAndProcessDupes(
+                                                                               TRANSID, FROMAPPID, g, source1, version,
+                                                                               nType, dupeList, dupeFixOn,
+                                                                               deleteCandidateList, singleCommits,     dupeGroups, loader);
+                                                       Iterator<String> dIter = tmpDupeGroups.iterator();
+                                                       while (dIter.hasNext()) {
+                                                               // Add in any newly found dupes to our running list
+                                                               String tmpGrp = dIter.next();
+                                                               logger.info("Found set of dupes: [" + tmpGrp + "]");
+                                                               dupeGroups.add(tmpGrp);
+                                                       }
+                                               }
+                                               
+                                       }// end of extra dupe check for non-dependent nodes
+                                       
                                        if ( (thisNtDeleteCount > 0) && singleCommits ) {
                                                // NOTE - the singleCommits option is not used in normal processing
                                                g.tx().commit();
@@ -666,9 +748,10 @@ public class DataGrooming {
                                                
                                        }
                                        thisNtDeleteCount = 0;
-                                       LOGGER.info( " Processed " + thisNtCount + " records for [" + nType + "], " + totalNodeCount + " total overall. " );
+                                       logger.info( " Processed " + thisNtCount + " records for [" + nType + "], " + totalNodeCount + " total overall. " );
                                        
                                }// While-loop for each node type
+                               
                        }// end of check to make sure we weren't only supposed to do edges
 
                
@@ -683,15 +766,15 @@ public class DataGrooming {
                        // --------------------------------------------------------------------------------------
 
                        // To do some strange checking - we need a second graph object
-                       LOGGER.debug("    ---- DEBUG --- about to open a SECOND graph (takes a little while)--------\n");
+                       logger.debug("    ---- DEBUG --- about to open a SECOND graph (takes a little while)--------\n");
                        // Note - graph2 just reads - but we want it to use a fresh connection to 
                        //      the database, so we are NOT using the CACHED DB CONFIG here.
-                       graph2 = TitanFactory.open(AAIConstants.REALTIME_DB_CONFIG);
+                       graph2 = JanusGraphFactory.open(new AAIGraphConfig.Builder(AAIConstants.REALTIME_DB_CONFIG).forService(DataGrooming.class.getSimpleName()).withGraphType("realtime2").buildConfiguration());
                        if (graph2 == null) {
                                String emsg = "null graph2 object in DataGrooming\n";
                                throw new AAIException("AAI_6101", emsg);
                        } else {
-                               LOGGER.debug("Got the graph2 object... \n");
+                               logger.debug("Got the graph2 object... \n");
                        }
                        g2 = graph2.newTransaction();
                        if (g2 == null) {
@@ -710,7 +793,7 @@ public class DataGrooming {
                        int counter = 0;
                        int lastShown = 0;
                        Iterator<Vertex> vItor2 = vertList.iterator();
-                       LOGGER.info(" Checking for bad edges  --- ");
+                       logger.info(" Checking for bad edges  --- ");
 
                        while (vItor2.hasNext()) {
                                Vertex v = null;
@@ -718,7 +801,9 @@ public class DataGrooming {
                                        try {
                                                v = vItor2.next();
                                        } catch (Exception vex) {
-                                               LOGGER.warn(">>> WARNING trying to get next vertex on the vItor2 ");
+                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                               logger.warn(">>> WARNING trying to get next vertex on the vItor2 ");
                                                continue;
                                        }
                                        
@@ -727,24 +812,27 @@ public class DataGrooming {
                                        try {
                                                thisVertId = v.id().toString();
                                        } catch (Exception ev) {
-                                               LOGGER.warn("WARNING when doing getId() on a vertex from our vertex list.  ");
+                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                               logger.warn("WARNING when doing getId() on a vertex from our vertex list.  ");
                                                continue;
                                        }
                                        if (ghostNodeHash.containsKey(thisVertId)) {
                                                // This is a phantom node, so don't try to use it
-                                               LOGGER.info(" >> Skipping edge check for edges from vertexId = "
+                                               logger.info(" >> Skipping edge check for edges from vertexId = "
                                                                                + thisVertId
                                                                                + ", since that guy is a Phantom Node");
                                                continue;
                                        }
                                        
                                        if( windowStartTime > 0 ){
-                                               // We only want to look at nodes that are created after a passed-in timestamp
-                                               Object objTimeStamp = v.property("aai-created-ts").orElse(null);
-                                               if( objTimeStamp != null ){
-                                                       long thisNodeCreateTime = (long)objTimeStamp;
-                                                       if( thisNodeCreateTime < windowStartTime ){
-                                                               // It is NOT in our window, so we can pass over it
+                                               // They are using the time-window, so we only want nodes that are updated after a
+                                               // passed-in timestamp OR that have no last-modified-timestamp which means they are suspicious.
+                                               Object objModTimeStamp = v.property("aai-last-mod-ts").orElse(null);
+                                               if( objModTimeStamp != null ){
+                                                       long thisNodeModTime = (long)objModTimeStamp;
+                                                       if( thisNodeModTime < windowStartTime ){
+                                                               // It has a last modified ts and is NOT in our window, so we can pass over it
                                                                continue;
                                                        }
                                                }
@@ -752,7 +840,7 @@ public class DataGrooming {
                                        
                                        if (counter == lastShown + 250) {
                                                lastShown = counter;
-                                               LOGGER.info("... Checking edges for vertex # "
+                                               logger.info("... Checking edges for vertex # "
                                                                + counter);
                                        }
                                        Iterator<Edge> eItor = v.edges(Direction.BOTH);
@@ -763,14 +851,18 @@ public class DataGrooming {
                                                try {
                                                        e = eItor.next();
                                                } catch (Exception iex) {
-                                                       LOGGER.warn(">>> WARNING trying to get next edge on the eItor ", iex);
+                                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                       logger.warn(">>> WARNING trying to get next edge on the eItor ", iex);
                                                        continue;
                                                }
 
                                                try {
                                                        vIn = e.inVertex();
                                                } catch (Exception err) {
-                                                       LOGGER.warn(">>> WARNING trying to get edge's In-vertex ", err);
+                                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                       logger.warn(">>> WARNING trying to get edge's In-vertex ", err);
                                                }
                                                String vNtI = "";
                                                String vIdI = "";
@@ -780,7 +872,7 @@ public class DataGrooming {
                                                Boolean cantGetUsingVid = false;
                                                if (vIn != null) {
                                                        try {
-                                                               Object ob = vIn.<Object>property("aai-node-type").orElse(null);
+                                                               Object ob = vIn.<Object>property(AAI_NODE_TYPE).orElse(null);
                                                                if (ob != null) {
                                                                        vNtI = ob.toString();
                                                                        keysMissing = anyKeyFieldsMissing(vNtI, vIn, loader);
@@ -795,7 +887,9 @@ public class DataGrooming {
                                                                if( ! ghost2CheckOff ){
                                                                        Vertex connectedVert = g2.traversal().V(vIdLong).next();
                                                                        if( connectedVert == null ) {
-                                                                               LOGGER.warn( "GHOST2 -- got NULL when doing getVertex for vid = " + vIdLong);
+                                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                               logger.warn( "GHOST2 -- got NULL when doing getVertex for vid = " + vIdLong);
                                                                                cantGetUsingVid = true;
                                                                                
                                                                                // If we can NOT get this ghost with the SECOND graph-object, 
@@ -805,7 +899,9 @@ public class DataGrooming {
                                                                                         ghost2 = g.traversal().V(vIdLong).next();
                                                                                }
                                                                                catch( Exception ex){
-                                                                                       LOGGER.warn( "GHOST2 --  Could not get the ghost info for a bad edge for vtxId = " + vIdLong, ex);
+                                                                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                                                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                                       logger.warn( "GHOST2 --  Could not get the ghost info for a bad edge for vtxId = " + vIdLong, ex);
                                                                                }
                                                                                if( ghost2 != null ){
                                                                                        ghostNodeHash.put(vIdI, ghost2);
@@ -814,7 +910,9 @@ public class DataGrooming {
                                                                }// end of the ghost2 checking
                                                        } 
                                                        catch (Exception err) {
-                                                               LOGGER.warn(">>> WARNING trying to get edge's In-vertex props ", err);
+                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                               logger.warn(">>> WARNING trying to get edge's In-vertex props ", err);
                                                        }
                                                }
                                                if (keysMissing || vIn == null || vNtI.equals("")
@@ -841,11 +939,13 @@ public class DataGrooming {
                                                                                deleteCount++;
                                                                        } catch (Exception e1) {
                                                                                okFlag = false;
-                                                                               LOGGER.warn("WARNING when trying to delete bad-edge-connected VERTEX VID = "
+                                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                               logger.warn("WARNING when trying to delete bad-edge-connected VERTEX VID = "
                                                                                                + vIdI, e1);
                                                                        }
                                                                        if (okFlag) {
-                                                                               LOGGER.info(" DELETED vertex from bad edge = "
+                                                                               logger.info(" DELETED vertex from bad edge = "
                                                                                                                + vIdI);
                                                                        }
                                                                } else {
@@ -864,11 +964,13 @@ public class DataGrooming {
                                                                                // that this edge has already been
                                                                                // removed
                                                                                okFlag = false;
-                                                                               LOGGER.warn("WARNING when trying to delete edge = "
+                                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                               logger.warn("WARNING when trying to delete edge = "
                                                                                                + thisEid);
                                                                        }
                                                                        if (okFlag) {
-                                                                               LOGGER.info(" DELETED edge = " + thisEid);
+                                                                               logger.info(" DELETED edge = " + thisEid);
                                                                        }
                                                                }
                                                        } else {
@@ -883,7 +985,9 @@ public class DataGrooming {
                                                try {
                                                        vOut = e.outVertex();
                                                } catch (Exception err) {
-                                                       LOGGER.warn(">>> WARNING trying to get edge's Out-vertex ");
+                                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                       logger.warn(">>> WARNING trying to get edge's Out-vertex ");
                                                }
                                                String vNtO = "";
                                                String vIdO = "";
@@ -892,7 +996,7 @@ public class DataGrooming {
                                                cantGetUsingVid = false;
                                                if (vOut != null) {
                                                        try {
-                                                               Object ob = vOut.<Object>property("aai-node-type").orElse(null);
+                                                               Object ob = vOut.<Object>property(AAI_NODE_TYPE).orElse(null);
                                                                if (ob != null) {
                                                                        vNtO = ob.toString();
                                                                        keysMissing = anyKeyFieldsMissing(vNtO,
@@ -909,13 +1013,15 @@ public class DataGrooming {
                                                                        Vertex connectedVert = g2.traversal().V(vIdLong).next();
                                                                        if( connectedVert == null ) {
                                                                                cantGetUsingVid = true;
-                                                                               LOGGER.info( "GHOST2 -- got NULL when doing getVertex for vid = " + vIdLong);
+                                                                               logger.info( "GHOST2 -- got NULL when doing getVertex for vid = " + vIdLong);
                                                                                // If we can get this ghost with the other graph-object, then get it -- it's still a ghost
                                                                                try {
                                                                                         ghost2 = g.traversal().V(vIdLong).next();
                                                                                }
                                                                                catch( Exception ex){
-                                                                                       LOGGER.warn( "GHOST2 -- Could not get the ghost info for a bad edge for vtxId = " + vIdLong, ex);
+                                                                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                                                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                                       logger.warn( "GHOST2 -- Could not get the ghost info for a bad edge for vtxId = " + vIdLong, ex);
                                                                                }
                                                                                if( ghost2 != null ){
                                                                                        ghostNodeHash.put(vIdO, ghost2);
@@ -923,17 +1029,19 @@ public class DataGrooming {
                                                                        }
                                                                }
                                                        } catch (Exception err) {
-                                                               LOGGER.warn(">>> WARNING trying to get edge's Out-vertex props ", err);
+                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                               logger.warn(">>> WARNING trying to get edge's Out-vertex props ", err);
                                                        }
                                                }
-                                               if (keysMissing || vOut == null || vNtO.equals("")
+                                               if (keysMissing || vOut == null || vNtO.isEmpty()
                                                                || cantGetUsingVid) {
                                                        // this is a bad edge because it points to a vertex
                                                        // that isn't there anymore
                                                        String thisEid = e.id().toString();
                                                        if (deleteCandidateList.contains(thisEid) || deleteCandidateList.contains(vIdO)) {
                                                                boolean okFlag = true;
-                                                               if (!vIdO.equals("")) {
+                                                               if (!vIdO.isEmpty()) {
                                                                        // try to get rid of the corrupted vertex
                                                                        try {
                                                                                if( (ghost2 != null) && ghost2FixOn ){
@@ -950,11 +1058,13 @@ public class DataGrooming {
                                                                                deleteCount++;
                                                                        } catch (Exception e1) {
                                                                                okFlag = false;
-                                                                               LOGGER.warn("WARNING when trying to delete bad-edge-connected VID = "
+                                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                               logger.warn("WARNING when trying to delete bad-edge-connected VID = "
                                                                                                + vIdO, e1);
                                                                        }
                                                                        if (okFlag) {
-                                                                               LOGGER.info(" DELETED vertex from bad edge = "
+                                                                               logger.info(" DELETED vertex from bad edge = "
                                                                                                                + vIdO);
                                                                        }
                                                                } else {
@@ -973,11 +1083,13 @@ public class DataGrooming {
                                                                                // that this edge has already been
                                                                                // removed
                                                                                okFlag = false;
-                                                                               LOGGER.warn("WARNING when trying to delete edge = "
+                                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                               logger.warn("WARNING when trying to delete edge = "
                                                                                                + thisEid, ex);
                                                                        }
                                                                        if (okFlag) {
-                                                                               LOGGER.info(" DELETED edge = " + thisEid);
+                                                                               logger.info(" DELETED edge = " + thisEid);
                                                                        }
                                                                }
                                                        } else {
@@ -990,7 +1102,9 @@ public class DataGrooming {
                                                }
                                        }// End of while-edges-loop
                                } catch (Exception exx) {
-                                       LOGGER.warn("WARNING from in the while-verts-loop ", exx);
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                       logger.warn("WARNING from in the while-verts-loop ", exx);
                                }
                        }// End of while-vertices-loop (the edge-checking)
                  }     // end of -- if we're not skipping the edge-checking 
@@ -999,12 +1113,14 @@ public class DataGrooming {
                        deleteCount = deleteCount + dupeGrpsDeleted;
                        if (!singleCommits && deleteCount > 0) {
                                try {
-                                       LOGGER.info("About to do the commit for "
+                                       logger.info("About to do the commit for "
                                                        + deleteCount + " removes. ");
                                        executeFinalCommit = true;
-                                       LOGGER.info("Commit was successful ");
+                                       logger.info("Commit was successful ");
                                } catch (Exception excom) {
-                                       LOGGER.error(" >>>> ERROR <<<<   Could not commit changes. ", excom);
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                       logger.error(" >>>> ERROR <<<<   Could not commit changes. " + LogFormatTools.getStackTop(excom));
                                        deleteCount = 0;
                                }
                        }
@@ -1018,7 +1134,14 @@ public class DataGrooming {
                        deleteCount = deleteCount + dupeGrpsDeleted;
 
                        bw.write("\n\n ============ Summary ==============\n");
-                       bw.write("Ran these nodeTypes: " + ntList + "\n\n");
+                       if( timeWindowMinutes == 0 ){
+                               bw.write("Ran FULL data grooming (no time-window). \n");
+                       }
+                       else {
+                               bw.write("Ran PARTIAL data grooming just looking at data added/updated in the last " + timeWindowMinutes + " minutes. \n");
+                       }
+                       
+                       bw.write("\nRan these nodeTypes: " + ntList + "\n\n");
                        bw.write("There were this many delete candidates from previous run =  "
                                        + deleteCandidateList.size() + "\n");
                        if (dontFixOrphansFlag) {
@@ -1084,7 +1207,9 @@ public class DataGrooming {
                                                bw.write(info + "\n");
                                        }
                                } catch (Exception dex) {
-                                       LOGGER.error("error trying to print detail info for a ghost-node:  ", dex);
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                       logger.error("error trying to print detail info for a ghost-node:  " + LogFormatTools.getStackTop(dex));
                                }
                        }
 
@@ -1106,7 +1231,9 @@ public class DataGrooming {
                                                bw.write(info + "\n");
                                        }
                                } catch (Exception dex) {
-                                       LOGGER.error("error trying to print detail info for a Orphan Node /missing dependent edge", dex);
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                       logger.error("error trying to print detail info for a Orphan Node /missing dependent edge " + LogFormatTools.getStackTop(dex));
                                }
                        }
 
@@ -1129,7 +1256,10 @@ public class DataGrooming {
                                                bw.write(info + "\n");
                                        }
                                } catch (Exception dex) {
-                                       LOGGER.error("error trying to print detail info for a node missing its dependent edge but not an orphan", dex);
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                       logger.error("error trying to print detail info for a node missing its dependent edge but not an orphan "
+                                                       + LogFormatTools.getStackTop(dex));
                                }
                        }
 
@@ -1149,7 +1279,9 @@ public class DataGrooming {
                                                                + propKey.value() + "]\n");
                                        }
                                } catch (Exception pex) {
-                                       LOGGER.error("error trying to print empty/bad vertex data: ", pex);
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                       logger.error("error trying to print empty/bad vertex data: " + LogFormatTools.getStackTop(pex));
                                }
                        }
 
@@ -1197,7 +1329,7 @@ public class DataGrooming {
                                                        // This is the last entry which should tell us if we
                                                        // have a preferred keeper
                                                        String prefString = dupeArr[i];
-                                                       if (prefString.equals("KeepVid=UNDETERMINED")) {
+                                                       if (KEEP_VID_UNDETERMINED.equals(prefString)) {
                                                                bw.write("\n For this group of duplicates, could not tell which one to keep.\n");
                                                                bw.write(" >>> This group needs to be taken care of with a manual/forced-delete.\n");
                                                        } else {
@@ -1205,7 +1337,7 @@ public class DataGrooming {
                                                                // should look like, "KeepVid=12345"
                                                                String[] prefArr = prefString.split("=");
                                                                if (prefArr.length != 2
-                                                                               || (!prefArr[0].equals("KeepVid"))) {
+                                                                               || (!"KeepVid".equals(prefArr[0]))) {
                                                                        throw new Exception("Bad format. Expecting KeepVid=999999");
                                                                } else {
                                                                        String keepVidStr = prefArr[1];
@@ -1228,7 +1360,9 @@ public class DataGrooming {
                                                }// else last entry
                                        }// for each vertex in a group
                                } catch (Exception dex) {
-                                       LOGGER.error("error trying to print duplicate vertex data", dex);
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                       logger.error("error trying to print duplicate vertex data " + LogFormatTools.getStackTop(dex));
                                }
 
                        }// while - work on each group of dupes
@@ -1242,14 +1376,14 @@ public class DataGrooming {
                        bw.write("\n ------------- Got these errors while processing: \n");
                        Iterator<String> errIter = errArr.iterator();
                        while (errIter.hasNext()) {
-                               String line = (String) errIter.next();
+                               String line = errIter.next();
                                bw.write(line + "\n");
                        }
 
                        bw.close();
 
-                       LOGGER.info("\n ------------- Done doing all the checks ------------ ");
-                       LOGGER.info("Output will be written to " + fullOutputFileName);
+                       logger.info("\n ------------- Done doing all the checks ------------ ");
+                       logger.info("Output will be written to " + fullOutputFileName);
 
                        if (cleanupCandidateCount > 0) {
                                // Technically, this is not an error -- but we're throwing this
@@ -1259,10 +1393,14 @@ public class DataGrooming {
                                                + "] and investigate delete candidates. ");
                        }
                } catch (AAIException e) {
-                       LOGGER.error("Caught AAIException while grooming data", e);
+                       LoggingContext.statusCode(StatusCode.ERROR);
+                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                       logger.error("Caught AAIException while grooming data");
                        ErrorLogHelper.logException(e);
                } catch (Exception ex) {
-                       LOGGER.error("Caught exception while grooming data", ex);
+                       LoggingContext.statusCode(StatusCode.ERROR);
+                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                       logger.error("Caught exception while grooming data");
                        ErrorLogHelper.logError("AAI_6128", ex.getMessage() + ", resolve and rerun dataGrooming");
                } finally {
 
@@ -1270,7 +1408,9 @@ public class DataGrooming {
                                try {
                                        bw.close();
                                } catch (IOException iox) {
-                                       LOGGER.warn("Got an IOException trying to close bufferedWriter() \n", iox);
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.AVAILABILITY_TIMEOUT_ERROR);
+                                       logger.warn("Got an IOException trying to close bufferedWriter() \n", iox);
                                }
                        }
 
@@ -1283,8 +1423,10 @@ public class DataGrooming {
                                        }
                                        g.tx().rollback();
                                } catch (Exception ex) {
-                                       // Don't throw anything because Titan sometimes is just saying that the graph is already closed
-                                       LOGGER.warn("WARNING from final graphTransaction.rollback()", ex);
+                                       // Don't throw anything because JanusGraph sometimes is just saying that the graph is already closed
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.AVAILABILITY_TIMEOUT_ERROR);
+                                       logger.warn("WARNING from final graphTransaction.rollback()", ex);
                                }
                        }
                        
@@ -1294,8 +1436,10 @@ public class DataGrooming {
                                try {
                                        g2.tx().rollback();
                                } catch (Exception ex) {
-                                       // Don't throw anything because Titan sometimes is just saying that the graph is already closed
-                                       LOGGER.warn("WARNING from final graphTransaction2.rollback()", ex);
+                                       // Don't throw anything because JanusGraph sometimes is just saying that the graph is already closed
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.AVAILABILITY_TIMEOUT_ERROR);
+                                       logger.warn("WARNING from final graphTransaction2.rollback()", ex);
                                }
                        }
                                
@@ -1306,8 +1450,10 @@ public class DataGrooming {
                                                graph.close();
                                        }
                                } catch (Exception ex) {
-                                       // Don't throw anything because Titan sometimes is just saying that the graph is already closed{
-                                       LOGGER.warn("WARNING from final graph.shutdown()", ex);
+                                       // Don't throw anything because JanusGraph sometimes is just saying that the graph is already closed{
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.AVAILABILITY_TIMEOUT_ERROR);
+                                       logger.warn("WARNING from final graph.shutdown()", ex);
                                }
                                
                                try {
@@ -1316,8 +1462,10 @@ public class DataGrooming {
                                                graph2.close();
                                        }
                                } catch (Exception ex) {
-                                       // Don't throw anything because Titan sometimes is just saying that the graph is already closed{
-                                       LOGGER.warn("WARNING from final graph2.shutdown()", ex);
+                                       // Don't throw anything because JanusGraph sometimes is just saying that the graph is already closed{
+                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                       LoggingContext.responseCode(LoggingContext.AVAILABILITY_TIMEOUT_ERROR);
+                                       logger.warn("WARNING from final graph2.shutdown()", ex);
                                }
                        }
                                
@@ -1391,7 +1539,7 @@ public class DataGrooming {
                        while (keyPropI.hasNext()) {
                                String propName = keyPropI.next();
                                Object ob = v.<Object>property(propName).orElse(null);
-                               if (ob == null || ob.toString().equals("")) {
+                               if (ob == null || ob.toString().isEmpty()) {
                                        // It is missing a key property
                                        return true;
                                }
@@ -1529,16 +1677,16 @@ public class DataGrooming {
 
                String vtxANodeType = "";
                String vtxBNodeType = "";
-               Object objType = vtxA.<Object>property("aai-node-type").orElse(null);
+               Object objType = vtxA.<Object>property(AAI_NODE_TYPE).orElse(null);
                if (objType != null) {
                        vtxANodeType = objType.toString();
                }
-               objType = vtxB.<Object>property("aai-node-type").orElse(null);
+               objType = vtxB.<Object>property(AAI_NODE_TYPE).orElse(null);
                if (objType != null) {
                        vtxBNodeType = objType.toString();
                }
 
-               if (vtxANodeType.equals("") || (!vtxANodeType.equals(vtxBNodeType))) {
+               if (vtxANodeType.isEmpty() || (!vtxANodeType.equals(vtxBNodeType))) {
                        // Either they're not really dupes or there's some bad data - so
                        // don't pick one
                        return nullVtx;
@@ -1549,10 +1697,11 @@ public class DataGrooming {
                // (We'll check dep-node later)
                // Determine what the key fields are for this nodeType
                Collection <String> keyProps = new ArrayList <>();
+               HashMap <String,Object> keyPropValsHash = new HashMap <String,Object>();
                try {
                        keyProps = loader.introspectorFromName(vtxANodeType).getKeys();
                } catch (AAIUnknownObjectException e) {
-                       LOGGER.warn("Required property not found", e);
+                       logger.warn("Required property not found", e);
                        throw new AAIException("AAI_6105", "Required Property name(s) not found for nodeType = " + vtxANodeType + ")");
                }
                
@@ -1576,6 +1725,11 @@ public class DataGrooming {
                                // data - so don't pick one
                                return nullVtx;
                        }
+                       else {
+                               // Keep these around for (potential) use later
+                               keyPropValsHash.put(propName, vtxAKeyPropVal);
+                       }
+                            
                }
 
                // Collect the vid's and aai-node-types of the vertices that each vertex
@@ -1592,7 +1746,7 @@ public class DataGrooming {
                                Vertex tvCon = iter.next();
                                String conVid = tvCon.id().toString();
                                String nt = "";
-                               objType = tvCon.<Object>property("aai-node-type").orElse(null);
+                               objType = tvCon.<Object>property(AAI_NODE_TYPE).orElse(null);
                                if (objType != null) {
                                        nt = objType.toString();
                                }
@@ -1608,7 +1762,7 @@ public class DataGrooming {
                                Vertex tvCon = iter.next();
                                String conVid = tvCon.id().toString();
                                String nt = "";
-                               objType = tvCon.<Object>property("aai-node-type").orElse(null);
+                               objType = tvCon.<Object>property(AAI_NODE_TYPE).orElse(null);
                                if (objType != null) {
                                        nt = objType.toString();
                                }
@@ -1618,18 +1772,20 @@ public class DataGrooming {
                }
 
                // 1 - If this kind of node needs a dependent node for uniqueness, then
-               // verify that they both nodes
-               // point to the same dependent node (otherwise they're not really
-               // duplicates)
+               //    verify that they both nodes point to the same dependent 
+               //    node (otherwise they're not really duplicates)
                // Note - there are sometimes more than one dependent node type since
-               // one nodeType can be used in
-               // different ways. But for a particular node, it will only have one
-               // dependent node that it's
-               // connected to.
-               Collection <String> depNodeTypes = loader.introspectorFromName(vtxANodeType).getDependentOn();
-                       
+               //    one nodeType can be used in different ways. But for a 
+               //    particular node, it will only have one dependent node that 
+               //    it's connected to.
+               String onlyNodeThatIndexPointsToVidStr = "";
+               Collection<String> depNodeTypes = loader.introspectorFromName(vtxANodeType).getDependentOn();
                if (depNodeTypes.isEmpty()) {
                        // This kind of node is not dependent on any other. That is ok.
+                       // We need to find out if the unique index info is good or not and
+                       // use that later when deciding if we can delete one.
+                       onlyNodeThatIndexPointsToVidStr = findJustOneUsingIndex( transId,
+                                       fromAppId, g, keyPropValsHash, vtxANodeType, vidA, vidB, ver );
                } else {
                        String depNodeVtxId4A = "";
                        String depNodeVtxId4B = "";
@@ -1645,7 +1801,7 @@ public class DataGrooming {
                                        depNodeVtxId4B = nodeTypesConn2B.get(depNodeType);
                                }
                        }
-                       if (depNodeVtxId4A.equals("")
+                       if (depNodeVtxId4A.isEmpty()
                                        || (!depNodeVtxId4A.equals(depNodeVtxId4B))) {
                                // Either they're not really dupes or there's some bad data - so
                                // don't pick either one
@@ -1654,8 +1810,11 @@ public class DataGrooming {
                }
 
                if (vtxIdsConn2A.size() == vtxIdsConn2B.size()) {
-                       // 2 - If they both have edges to all the same vertices, then return
-                       // the one with the lower vertexId.
+                       // 2 - If they both have edges to all the same vertices, 
+                       //  then return the one that can be reached uniquely via the 
+                       //  key if that is the case or
+                       //  else the one with the lower vertexId
+                       
                        boolean allTheSame = true;
                        Iterator<String> iter = vtxIdsConn2A.iterator();
                        while (iter.hasNext()) {
@@ -1667,7 +1826,19 @@ public class DataGrooming {
                        }
 
                        if (allTheSame) {
-                               if (vidA < vidB) {
+                               // If everything is the same, but one of the two has a good 
+                               // pointer to it, then save that one.  Otherwise, take the
+                               // older one.
+                               if( !onlyNodeThatIndexPointsToVidStr.isEmpty() ){
+                                       // only one is reachable via the index - choose that one.
+                                       if( onlyNodeThatIndexPointsToVidStr.equals(vidA.toString()) ){
+                                               preferredVtx = vtxA;
+                                       }
+                                       else if( onlyNodeThatIndexPointsToVidStr.equals(vidB.toString()) ){
+                                               preferredVtx = vtxB;
+                                       }
+                               }
+                               else if (vidA < vidB) {
                                        preferredVtx = vtxA;
                                } else {
                                        preferredVtx = vtxB;
@@ -1675,7 +1846,8 @@ public class DataGrooming {
                        }
                } else if (vtxIdsConn2A.size() > vtxIdsConn2B.size()) {
                        // 3 - VertexA is connected to more things than vtxB.
-                       // We'll pick VtxA if its edges are a superset of vtxB's edges.
+                       // We'll pick VtxA if its edges are a superset of vtxB's edges 
+                       //   and it doesn't contradict the check for the index/key pointer.
                        boolean missingOne = false;
                        Iterator<String> iter = vtxIdsConn2B.iterator();
                        while (iter.hasNext()) {
@@ -1686,11 +1858,15 @@ public class DataGrooming {
                                }
                        }
                        if (!missingOne) {
-                               preferredVtx = vtxA;
+                               if( onlyNodeThatIndexPointsToVidStr.isEmpty()
+                                               || onlyNodeThatIndexPointsToVidStr.equals(vidA.toString()) ){
+                                       preferredVtx = vtxA;
+                               }
                        }
                } else if (vtxIdsConn2B.size() > vtxIdsConn2A.size()) {
                        // 4 - VertexB is connected to more things than vtxA.
-                       // We'll pick VtxB if its edges are a superset of vtxA's edges.
+                       // We'll pick VtxB if its edges are a superset of vtxA's edges
+                       //   and it doesn't contradict the check for the index/key pointer.
                        boolean missingOne = false;
                        Iterator<String> iter = vtxIdsConn2A.iterator();
                        while (iter.hasNext()) {
@@ -1701,7 +1877,10 @@ public class DataGrooming {
                                }
                        }
                        if (!missingOne) {
-                               preferredVtx = vtxB;
+                               if( onlyNodeThatIndexPointsToVidStr.isEmpty()
+                                               || onlyNodeThatIndexPointsToVidStr.equals(vidB.toString()) ){
+                                       preferredVtx = vtxB;
+                               }
                        }
                } else {
                        preferredVtx = nullVtx;
@@ -1724,7 +1903,6 @@ public class DataGrooming {
         * @param deleteCandidateList the delete candidate list
         * @param singleCommits the single commits
         * @param alreadyFoundDupeGroups the already found dupe groups
-        * @param dbMaps the db maps
         * @return the array list
         */
        private static List<String> checkAndProcessDupes(String transId,
@@ -1798,15 +1976,14 @@ public class DataGrooming {
                                String dupesStr = "";
                                for (int i = 0; i < checkVertList.size(); i++) {
                                        dupesStr = dupesStr
-                                                       + ((checkVertList.get(i))).id()
-                                                                       .toString() + "|";
+                                                       + checkVertList.get(i).id().toString() + "|";
                                }
                                if (dupesStr != "") {
                                        Vertex prefV = getPreferredDupe(transId, fromAppId,
                                                        source, checkVertList, version, loader);
                                        if (prefV == null) {
                                                // We could not determine which duplicate to keep
-                                               dupesStr = dupesStr + "KeepVid=UNDETERMINED";
+                                               dupesStr = dupesStr + KEEP_VID_UNDETERMINED;
                                                returnList.add(dupesStr);
                                        } else {
                                                dupesStr = dupesStr + "KeepVid=" + prefV.id();
@@ -1845,8 +2022,7 @@ public class DataGrooming {
                                                String dupesStr = "";
                                                for (int i = 0; i < thisParentsVertList.size(); i++) {
                                                        dupesStr = dupesStr
-                                                                       + ((thisParentsVertList
-                                                                                       .get(i))).id() + "|";
+                                                                       + thisParentsVertList.get(i).id() + "|";
                                                }
                                                if (dupesStr != "") {
                                                        Vertex prefV = getPreferredDupe(transId,
@@ -1856,7 +2032,7 @@ public class DataGrooming {
                                                        if (prefV == null) {
                                                                // We could not determine which duplicate to
                                                                // keep
-                                                               dupesStr = dupesStr + "KeepVid=UNDETERMINED";
+                                                               dupesStr = dupesStr + KEEP_VID_UNDETERMINED;
                                                                returnList.add(dupesStr);
                                                        } else {
                                                                Boolean didRemove = false;
@@ -1880,7 +2056,9 @@ public class DataGrooming {
                                }
                        }
                } catch (Exception e) {
-                       LOGGER.warn(" >>> Threw an error in checkAndProcessDupes - just absorb this error and move on. ", e);
+                       LoggingContext.statusCode(StatusCode.ERROR);
+                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                       logger.warn(" >>> Threw an error in checkAndProcessDupes - just absorb this error and move on. ", e);
                }
 
                return returnList;
@@ -1896,7 +2074,6 @@ public class DataGrooming {
         * @param version the version
         * @param nType the n type
         * @param passedVertList the passed vert list
-        * @param dbMaps the db maps
         * @return the hash map
         * @throws AAIException the AAI exception
         */
@@ -1904,7 +2081,7 @@ public class DataGrooming {
                        String transId, String fromAppId, GraphTraversalSource g, String version,
                        String nType, ArrayList<Vertex> passedVertList, Loader loader)
                        throws AAIException {
-               // Given a list of Titan Vertices of one nodeType (see AAI-8956), group 
+               // Given a list of JanusGraph Vertices of one nodeType (see AAI-8956), group 
                // them together by the parent node they depend on.
                // Ie. if given a list of ip address nodes (assumed to all have the
                // same key info) they might sit under several different parent vertices.
@@ -1937,7 +2114,7 @@ public class DataGrooming {
                                Vertex tmpParentVtx = getConnectedParent( g, thisVert );
                                if( tmpParentVtx != null ) {
                                        String parentNt = null;
-                                       Object obj = tmpParentVtx.<Object>property("aai-node-type").orElse(null);
+                                       Object obj = tmpParentVtx.<Object>property(AAI_NODE_TYPE).orElse(null);
                                        if (obj != null) {
                                                parentNt = obj.toString();
                                        }
@@ -1980,7 +2157,7 @@ public class DataGrooming {
                // This assumes that the dupeInfoString is in the format of
                // pipe-delimited vid's followed by
                // ie. "3456|9880|keepVid=3456"
-               if (deleteCandidateList == null || deleteCandidateList.size() == 0) {
+               if (deleteCandidateList == null || deleteCandidateList.isEmpty()) {
                        // No vid's on the candidate list -- so no deleting will happen on
                        // this run
                        return false;
@@ -1998,7 +2175,7 @@ public class DataGrooming {
                                // This is the last entry which should tell us if we have a
                                // preferred keeper
                                String prefString = dupeArr[i];
-                               if (prefString.equals("KeepVid=UNDETERMINED")) {
+                               if (prefString.equals(KEEP_VID_UNDETERMINED)) {
                                        // They sent us a bad string -- nothing should be deleted if
                                        // no dupe could be tagged as preferred
                                        return false;
@@ -2006,8 +2183,10 @@ public class DataGrooming {
                                        // If we know which to keep, then the prefString should look
                                        // like, "KeepVid=12345"
                                        String[] prefArr = prefString.split("=");
-                                       if (prefArr.length != 2 || (!prefArr[0].equals("KeepVid"))) {
-                                               LOGGER.error("Bad format. Expecting KeepVid=999999");
+                                       if (prefArr.length != 2 || (!"KeepVid".equals(prefArr[0]))) {
+                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                               logger.error("Bad format. Expecting KeepVid=999999");
                                                return false;
                                        } else {
                                                String keepVidStr = prefArr[1];
@@ -2035,16 +2214,20 @@ public class DataGrooming {
                                                                                }
                                                                        } catch (Exception e) {
                                                                                okFlag = false;
-                                                                               LOGGER.error("ERROR trying to delete VID = " + thisVid, e);
+                                                                               LoggingContext.statusCode(StatusCode.ERROR);
+                                                                               LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                                               logger.error("ERROR trying to delete VID = " + thisVid + " " + LogFormatTools.getStackTop(e));
                                                                        }
                                                                        if (okFlag) {
-                                                                               LOGGER.info(" DELETED VID = " + thisVid);
+                                                                               logger.info(" DELETED VID = " + thisVid);
                                                                                deletedSomething = true;
                                                                        }
                                                                }
                                                        }
                                                } else {
-                                                       LOGGER.error("ERROR - Vertex Id to keep not found in list of dupes.  dupeInfoString = ["
+                                                       LoggingContext.statusCode(StatusCode.ERROR);
+                                                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                                                       logger.error("ERROR - Vertex Id to keep not found in list of dupes.  dupeInfoString = ["
                                                                        + dupeInfoString + "]");
                                                        return false;
                                                }
@@ -2098,32 +2281,34 @@ public class DataGrooming {
                try { 
                        if( topPropIndex == 0 ){
                                propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ") ";
-                               verts= graph.V().has(kName.get(0),kVal.get(0)).has("aai-node-type",nodeType);   
+                               verts= graph.V().has(kName.get(0),kVal.get(0)).has(AAI_NODE_TYPE,nodeType);
                        }       
                        else if( topPropIndex == 1 ){
                                propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
                                                + kName.get(1) + " = " + kVal.get(1) + ") ";
-                               verts =  graph.V().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has("aai-node-type",nodeType);   
+                               verts =  graph.V().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(AAI_NODE_TYPE,nodeType);
                        }                       
                        else if( topPropIndex == 2 ){
                                propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
                                                + kName.get(1) + " = " + kVal.get(1) + ", " 
                                                + kName.get(2) + " = " + kVal.get(2) +  ") ";
-                               verts= graph.V().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has("aai-node-type",nodeType);
-                       }       
+                               verts= graph.V().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has(AAI_NODE_TYPE,nodeType);
+                       }
                        else if( topPropIndex == 3 ){
                                propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
                                                + kName.get(1) + " = " + kVal.get(1) + ", " 
                                                + kName.get(2) + " = " + kVal.get(2) + ", " 
                                                + kName.get(3) + " = " + kVal.get(3) +  ") ";
-                               verts= graph.V().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has(kName.get(3),kVal.get(3)).has("aai-node-type",nodeType);
+                               verts= graph.V().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has(kName.get(3),kVal.get(3)).has(AAI_NODE_TYPE,nodeType);
                        }                       
                        else {
                                throw new AAIException("AAI_6114", " We only support 4 keys per nodeType for now \n"); 
                        }
                }
                catch( Exception ex ){
-                       LOGGER.error( " ERROR trying to get node for: [" + propsAndValuesForMsg + "]", ex);
+                       LoggingContext.statusCode(StatusCode.ERROR);
+                       LoggingContext.responseCode(LoggingContext.DATA_ERROR);
+                       logger.error( " ERROR trying to get node for: [" + propsAndValuesForMsg + "]" + LogFormatTools.getStackTop(ex));
                }
 
                if( verts != null ){
@@ -2133,8 +2318,8 @@ public class DataGrooming {
                        }
                }
                
-               if( retVertList.size() == 0 ){
-                       LOGGER.debug("DEBUG No node found for nodeType = [" + nodeType +
+               if( retVertList.isEmpty() ){
+                       logger.debug("DEBUG No node found for nodeType = [" + nodeType +
                                        "], propsAndVal = " + propsAndValuesForMsg );
                }
                
@@ -2170,7 +2355,7 @@ public class DataGrooming {
                                retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< ");
                        }
                        else {
-                               String nType = vtx.<String>property("aai-node-type").orElse(null);
+                               String nType = vtx.<String>property(AAI_NODE_TYPE).orElse(null);
                                String vid = vtx.id().toString();
                                retArr.add("Found an IN edge (" + lab + ") to this vertex from a [" + nType + "] node with VtxId = " + vid );
                                
@@ -2194,7 +2379,7 @@ public class DataGrooming {
                                retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< ");
                        }
                        else {
-                               String nType = vtx.<String>property("aai-node-type").orElse(null);
+                               String nType = vtx.<String>property(AAI_NODE_TYPE).orElse(null);
                                String vid = vtx.id().toString();
                                retArr.add("Found an OUT edge (" + lab + ") from this vertex to a [" + nType + "] node with VtxId = " + vid );
                        }
@@ -2219,7 +2404,7 @@ public class DataGrooming {
                }
                else {
                        String nodeType = "";
-                       Object ob = tVert.<Object>property("aai-node-type").orElse(null);
+                       Object ob = tVert.<Object>property(AAI_NODE_TYPE).orElse(null);
                        if( ob == null ){
                                nodeType = "null";
                        }
@@ -2263,7 +2448,7 @@ public class DataGrooming {
        
 
        private static ArrayList <Vertex> getConnectedChildrenOfOneType( GraphTraversalSource g, 
-                       Vertex startVtx, String childNType ) throws AAIException{
+                       Vertex startVtx, String childNType ) {
                
                ArrayList <Vertex> childList = new ArrayList <> ();
                Iterator <Vertex> vertI =  g.V(startVtx).union(__.outE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.OUT.toString()).inV(), __.inE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.IN.toString()).outV());
@@ -2271,7 +2456,7 @@ public class DataGrooming {
                Vertex tmpVtx = null;
                while( vertI != null && vertI.hasNext() ){
                        tmpVtx = vertI.next();
-                       Object ob = tmpVtx.<Object>property("aai-node-type").orElse(null);
+                       Object ob = tmpVtx.<Object>property(AAI_NODE_TYPE).orElse(null);
                        if (ob != null) {
                                String tmpNt = ob.toString();
                                if( tmpNt.equals(childNType)){
@@ -2286,11 +2471,11 @@ public class DataGrooming {
 
 
        private static Vertex getConnectedParent( GraphTraversalSource g, 
-                       Vertex startVtx ) throws AAIException{
+                       Vertex startVtx ) {
                
                Vertex parentVtx = null;
                Iterator <Vertex> vertI = g.V(startVtx).union(__.inE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.OUT.toString()).outV(), __.outE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.IN.toString()).inV());
-                               
+
                while( vertI != null && vertI.hasNext() ){
                        // Note - there better only be one!
                        parentVtx = vertI.next();
@@ -2311,10 +2496,150 @@ public class DataGrooming {
                long unixTimeNow = System.currentTimeMillis();
                long windowInMillis = timeWindowMinutes * 60L * 1000;
                
-               long startTimeStamp = unixTimeNow - windowInMillis;
-               
-               return startTimeStamp;
+               return unixTimeNow - windowInMillis;
        } // End of figureWindowStartTime()
        
        
+       /**
+        * Collect Duplicate Sets for nodes that are NOT dependent on parent nodes.
+        *
+        * @param transId the trans id
+        * @param fromAppId the from app id
+        * @param g the g
+        * @param version the version
+        * @param nType the n type
+        * @param passedVertList the passed vert list
+        * @return the array list
+        */
+       private static ArrayList<ArrayList<Vertex>> getDupeSets4NonDepNodes( String transId,
+                       String fromAppId, Graph g, String version, String nType,
+                       ArrayList<Vertex> passedVertList,
+                       ArrayList <String> keyPropNamesArr, 
+                        Loader loader ) {
+               
+               ArrayList<ArrayList<Vertex>> returnList = new ArrayList<ArrayList<Vertex>>();
+               
+               // We've been passed a set of nodes that we want to check. 
+               // They are all NON-DEPENDENT nodes of the same nodeType meaning that they should be 
+               // unique in the DB based on their KEY DATA alone.  So, if
+               // we group them by their key data - if any key has more than one
+               // vertex mapped to it, those vertices are dupes.
+               //
+               // When we find duplicates, we group them in an ArrayList (there can be
+               //     more than one duplicate for one set of key data)
+               // Then these dupeSets are grouped up and returned.
+               // 
+               
+               HashMap <String, ArrayList<String>> keyVals2VidHash = new HashMap <String, ArrayList<String>>();
+               HashMap <String,Vertex> vtxHash = new HashMap <String,Vertex>();
+               Iterator<Vertex> pItr = passedVertList.iterator();
+               while (pItr.hasNext()) {
+                       try {
+                               Vertex tvx =  pItr.next();
+                               String thisVid = tvx.id().toString();
+                               vtxHash.put(thisVid, tvx);
+                               
+                               // if there are more than one vertexId mapping to the same keyProps -- they are dupes
+                               // we dont check till later since a set can contain more than 2.
+                               String hKey = getNodeKeyValString( tvx, keyPropNamesArr );
+                               if( keyVals2VidHash.containsKey(hKey) ){
+                                       // We've already seen this key 
+                                       ArrayList <String> tmpVL = (ArrayList <String>)keyVals2VidHash.get(hKey);
+                                       tmpVL.add(thisVid);
+                                       keyVals2VidHash.put(hKey, tmpVL);
+                               }
+                               else {
+                                       // First time for this key
+                                       ArrayList <String> tmpVL = new ArrayList <String>();
+                                       tmpVL.add(thisVid);
+                                       keyVals2VidHash.put(hKey, tmpVL);
+                               }
+                       }
+                       catch (Exception e) {
+                               logger.warn(" >>> Threw an error in getDupeSets4NonDepNodes - just absorb this error and move on. ", e);
+                       }
+               }
+                                       
+               for( Map.Entry<String, ArrayList<String>> entry : keyVals2VidHash.entrySet() ){
+                       ArrayList <String> vidList = entry.getValue();
+                       try {
+                               if( !vidList.isEmpty() && vidList.size() > 1 ){
+                                       // There are more than one vertex id's using the same key info
+                                       ArrayList <Vertex> vertList = new ArrayList <Vertex> ();
+                                       for (int i = 0; i < vidList.size(); i++) {
+                                               String tmpVid = vidList.get(i);
+                                               vertList.add(vtxHash.get(tmpVid));
+                                       }
+                                       returnList.add(vertList);
+                               }
+                       } 
+                       catch (Exception e) {
+                               logger.warn(" >>> Threw an error in getDupeSets4NonDepNodes - just absorb this error and move on. ", e);
+                       }
+                       
+               }
+               return returnList;
+
+       }// End of getDupeSets4NonDepNodes()
+       
+       
+       /**
+        * Get values of the key properties for a node as a single string
+        *
+        * @param tvx the vertex to pull the properties from
+        * @param keyPropNamesArr collection of key prop names
+        * @return a String of concatenated values
+        */
+       private static String getNodeKeyValString( Vertex tvx,
+                       ArrayList <String> keyPropNamesArr ) {
+               
+               String retString = "";
+               Iterator <String> propItr = keyPropNamesArr.iterator();
+               while( propItr.hasNext() ){
+                       String propName = propItr.next();
+                       if( tvx != null ){
+                               Object propValObj = tvx.property(propName).orElse(null);
+                               retString = " " + retString + propValObj.toString();
+                       } 
+               }
+               return retString;
+       
+       }// End of getNodeKeyValString()        
+
+
+       private static String findJustOneUsingIndex( String transId, String fromAppId,
+                       GraphTraversalSource gts, HashMap <String,Object> keyPropValsHash, 
+                       String nType, Long vidAL, Long vidBL, String apiVer){
+               
+               // See if querying by JUST the key params (which should be indexed) brings back
+               // ONLY one of the two vertices. Ie. the db still has a pointer to one of them
+               // and the other one is sort of stranded.
+               String returnVid = "";
+               
+               try {
+                       List <Vertex> tmpVertList = getNodeJustUsingKeyParams( transId, fromAppId, gts,
+                                       nType, keyPropValsHash, apiVer );
+                       if( tmpVertList != null && tmpVertList.size() == 1 ){
+                               // We got just one - if it matches one of the ones we're looking
+                               // for, then return that VID
+                               Vertex tmpV = tmpVertList.get(0);
+                               String thisVid = tmpV.id().toString();
+                               if( thisVid.equals(vidAL.toString()) || thisVid.equals(vidBL.toString()) ){
+                                       String msg = " vid = " + thisVid + " is one of two that the DB can retrieve directly ------";
+
+                                       logger.info(msg);
+                                       returnVid = thisVid;
+                               }
+                       }
+               }
+               catch ( AAIException ae ){
+                       String emsg = "Error trying to get node just by key " + ae.getMessage();
+
+                       logger.error(emsg);
+               }
+               
+               return returnVid;
+               
+       }// End of findJustOneUsingIndex()
+
 }