/* * Copyright (c) 2008-2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.workflow; import java.net.URI; import java.util.Iterator; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.Workflow; import com.emc.storageos.db.client.model.WorkflowStep; import com.emc.storageos.db.client.model.WorkflowStepData; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.services.util.NamedScheduledThreadPoolExecutor; /** * Marks Workflows and their WorkflowSteps inactive if older than WORKFLOW_HOLDING_TIME * (30 days). * (Modeled after TaskScrubberExecutor by David Maddison). * * @author watson */ public class WorkflowScrubberExecutor { private static final Logger log = LoggerFactory.getLogger(WorkflowScrubberExecutor.class); private ScheduledExecutorService _executor = new NamedScheduledThreadPoolExecutor("WorkflowScrubber", 1); private DbClient dbClient; private static final Long DAYS_IN_MSEC = 24L * 3600 * 1000; public static final Long WORKFLOW_HOLDING_TIME_MSEC = 30L * DAYS_IN_MSEC; // 30 days public void start() { log.info("Starting WorkflowScrubber"); _executor.scheduleWithFixedDelay( new Runnable() { public void run() { try { deleteOldWorkflows(); } catch(Exception e) { log.error("Exception thrown while scrubbing workflows"); log.error(e.getMessage(), e); } } }, 1, 24, TimeUnit.HOURS); } /** * Scan all the workflows, delete: * workflows older than WORKFLOW_HOLDING_TIME_MSEC * workflow steps associated with any workflow deleted * workflowStepData associated with any workflow deleted * Also finds and deletes: * orphaned workflow steps (steps without a valid workflow) * orphaned workflowStepData (without a workflow) */ public void deleteOldWorkflows() { log.info("Scanning for old workflows to be deleted"); List<URI> workflowURIs = dbClient.queryByType(Workflow.class, true); Iterator<Workflow> workflowItr = dbClient.queryIterativeObjects(Workflow.class, workflowURIs); Long currentTime = System.currentTimeMillis(); int workflowCount = 0, workflowsDeletedCount = 0, stepsDeletedCount = 0, stepDataDeletedCount = 0; while (workflowItr.hasNext()) { workflowCount++; Workflow workflow = workflowItr.next(); URI uri = workflow.getId(); try { Long creationTime = (workflow.getCreationTime() == null) ? (currentTime - WORKFLOW_HOLDING_TIME_MSEC) : workflow.getCreationTime().getTimeInMillis(); Long age = currentTime - creationTime; if (age >= WORKFLOW_HOLDING_TIME_MSEC) { log.info("Processing workflow {} age (msec) {}", uri, age); // Find all the WorkflowSteps for this Workflow, and them mark them for deletion. URIQueryResultList stepURIs = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory.getWorkflowWorkflowStepConstraint(uri), stepURIs); Iterator<WorkflowStep> wfStepItr = dbClient.queryIterativeObjects(WorkflowStep.class, stepURIs); while (wfStepItr.hasNext()) { WorkflowStep step = wfStepItr.next(); URI stepURI = step.getId(); stepsDeletedCount++; dbClient.removeObject(step); log.info("Workflow step {} for workflow {} marked inactive", stepURI, uri); } // Find all the WorkflowStepData for this Workflow, and them mark them for deletion. URIQueryResultList stepDataURIs = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory.getWorkflowStepDataConstraint(uri), stepDataURIs); Iterator<WorkflowStepData> wfStepDataItr = dbClient.queryIterativeObjects(WorkflowStepData.class, stepDataURIs); while (wfStepDataItr.hasNext()) { WorkflowStepData stepData = wfStepDataItr.next(); stepDataDeletedCount++; dbClient.removeObject(stepData); log.info("Workflow step data {} for workflow {} marked inactive", stepData.getId(), uri); } // Mark the workflow itself for deletion if (!workflow.getInactive()) { workflowsDeletedCount++; dbClient.removeObject(workflow); log.info("Workflow {} marked inactive", uri); } } } catch (Exception ex) { log.error("Exception processing workflow: " + uri, ex); } } // now query workflow steps and clean up any orphaned steps Iterator<WorkflowStep> workflowStepItr = dbClient.queryIterativeObjects(WorkflowStep.class, dbClient.queryByType(WorkflowStep.class, true)); while (workflowStepItr.hasNext()) { WorkflowStep step = workflowStepItr.next(); if (NullColumnValueGetter.isNullURI(step.getWorkflowId())) { // step is orphaned -- delete it stepsDeletedCount++; dbClient.removeObject(step); log.info("Orphaned workflow step {} marked inactive", step.getId()); } else { Workflow wf = dbClient.queryObject(Workflow.class, step.getWorkflowId()); if (wf == null || wf.getInactive()) { // step is orphaned -- delete it stepsDeletedCount++; dbClient.removeObject(step); log.info("Orphaned workflow step {} marked inactive", step.getId()); } } } // now query workflow step data and clean up any orphaned step data Iterator<WorkflowStepData> workflowStepDataItr = dbClient.queryIterativeObjects(WorkflowStepData.class, dbClient.queryByType(WorkflowStepData.class, true)); while (workflowStepDataItr.hasNext()) { WorkflowStepData stepData = workflowStepDataItr.next(); if (NullColumnValueGetter.isNullURI(stepData.getWorkflowId())) { // step data is orphaned -- delete it stepDataDeletedCount++; dbClient.removeObject(stepData); log.info("Orphaned workflow step data {} marked inactive", stepData.getId()); } else { Workflow wf = dbClient.queryObject(Workflow.class, stepData.getWorkflowId()); if (wf == null || wf.getInactive()) { // step data is orphaned -- delete it stepDataDeletedCount++; dbClient.removeObject(stepData); log.info("Orphaned workflow step data {} marked inactive", stepData.getId()); } } } log.info( "Done scanning for old workflows; {} workflows analyzed; {} old workflows deleted; {} workflow steps deleted; {} workflow step data deleted", workflowCount, workflowsDeletedCount, stepsDeletedCount, stepDataDeletedCount); } public DbClient getDbClient() { return dbClient; } public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } }