/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.db.gc; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.services.util.NamedThreadPoolExecutor; import org.apache.curator.framework.recipes.locks.InterProcessLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.model.DataObject; import com.emc.storageos.db.common.DependencyTracker; /** * Class schedules and runs GC job */ abstract class GarbageCollectionExecutorLoop implements Runnable { private static final Logger log = LoggerFactory.getLogger(GarbageCollectionExecutor.class); protected DependencyTracker dependencyTracker; /** * Duration after marked inactive, before GC can process an item * this needs to be enough to cover operations in flight */ private static final int DEFAULT_GC_SWEEP_DELAY_MIN = 10; protected int gcDelayMins = DEFAULT_GC_SWEEP_DELAY_MIN; protected DbClient dbClient; protected CoordinatorClient coordinator; protected String dbServiceId; private List<Future> futures = new ArrayList(); private ExecutorService executorPool = new NamedThreadPoolExecutor(GarbageCollectionExecutorLoop.class.getSimpleName(), 5); GarbageCollectionExecutorLoop() { } public void setDependencyTracker(DependencyTracker dependencyTracker) { this.dependencyTracker = dependencyTracker; } public void setGcDelayMins(int gcDelayMins) { this.gcDelayMins = gcDelayMins; } public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } public void setCoordinator(CoordinatorClient coordinator) { this.coordinator = coordinator; } public void setDbServiceId(String dbServiceId) { this.dbServiceId = dbServiceId; } // return true if this GC thread could be run now protected abstract boolean preGC(); /** * * @param clazz the GC will run on * @param <T> * @return true if we can run GC on this class **/ protected abstract <T extends DataObject> boolean canRunGCOnClass(Class<T> clazz); /** * * @param clazz the GC will run on * @param <T> **/ protected abstract <T extends DataObject> GarbageCollectionRunnable genGCTask(Class<T> clazz); protected abstract void postGC(); /** * return ZK lock name for GC * @return */ protected abstract String getGCZKLockName(); @Override public void run() { InterProcessLock lock = null; try { lock = getLockForGC(); if (lock == null) { log.info("Can't get GC lock, wait for next run."); return; } } catch (Exception e) { log.warn("Failed to acquire ZK lock for GC", e); return; } long beginTime = System.currentTimeMillis(); log.info("Begin to GC..."); try { if (!preGC()) { return; // can't run GC now } int maxLevels = dependencyTracker.getLevels(); for (int i = 0; i < maxLevels; i++) { log.info("Now processing level {}", i); List<Class<? extends DataObject>> list = new ArrayList(dependencyTracker.getTypesInLevel(i)); Collections.shuffle(list); for (Class<? extends DataObject> clazz : list) { if (canRunGCOnClass(clazz)) { GarbageCollectionRunnable gc = genGCTask(clazz); futures.add(executorPool.submit(gc)); } } waitTasksToComplete(); } } finally { try { postGC(); } finally { releaseLockForGC(lock); log.info("GC is finished, consume time: {} seconds", (System.currentTimeMillis() - beginTime) / 1000); } } } /** * Post handling after GC **/ private void waitTasksToComplete() { log.info("Waiting for {} tasks to finish", futures.size()); for (Future f : futures) { try { f.get(); }catch (InterruptedException ex) { log.warn("The GC was interrupted e=", ex); } catch (ExecutionException ex) { log.error("Exception caught: ", ex); } } futures.clear(); log.info("GC tasks are done"); } private InterProcessLock getLockForGC() { InterProcessLock lock = null; String lockName = getGCZKLockName(); try { log.info("try to get ZK GC lock {}", lockName); lock = coordinator.getLock(lockName); if (!lock.acquire(0, TimeUnit.SECONDS)) {// try to get the lock timeout=0 log.info("Can't get ZK lock for GC"); return null; // failed to get the lock } log.info("Get GC lock {}", lockName); } catch (Exception e) { log.warn("Failed to acquire lock for GC {} Exception e=", lockName, e); lock = null; } return lock; } private void releaseLockForGC(InterProcessLock lock) { String lockName = getGCZKLockName(); try { lock.release(); log.info("Release the ZK lock of {}", lockName); } catch (Exception e) { log.error("Failed to release the lock for GC {} e=", lockName, e); } } }