package com.emc.storageos.coordinator.client.service.impl; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.coordinator.client.service.DistributedAroundHook; import com.emc.storageos.coordinator.client.service.DistributedLockQueueManager; import com.emc.storageos.coordinator.client.service.LeaderSelectorListenerForPeriodicTask; import org.apache.curator.framework.recipes.leader.LeaderSelector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Set; /** * Scheduled periodic task runner for a {@link DistributedLockQueueManager}, to be executed exclusively on a leader * using leader-election. * * @author Ian Bibby */ public class DistributedLockQueueScheduler { private static final Logger log = LoggerFactory.getLogger(DistributedLockQueueScheduler.class); private static final String LOCKQUEUE_LEADER_PATH = "lockqueueleader"; private static final int INITIAL_DELAY = 300; // 5 minutes private static final int INTERVAL = 300; private CoordinatorClient coordinator; private DistributedLockQueueManager lockQueue; private LeaderSelector leaderSelector; private LeaderSelectorListenerForPeriodicTask listener; private DequeueValidator validator; public void setCoordinator(CoordinatorClient coordinator) { this.coordinator = coordinator; } public void setLockQueue(DistributedLockQueueManager lockQueue) { this.lockQueue = lockQueue; } public void setValidator(DequeueValidator validator) { this.validator = validator; } public void start() { listener = new LeaderSelectorListenerForPeriodicTask(new LockQueuePeriodicTask(), INITIAL_DELAY, INTERVAL); leaderSelector = coordinator.getLeaderSelector(LOCKQUEUE_LEADER_PATH, listener); leaderSelector.autoRequeue(); leaderSelector.start(); } public void stop() { leaderSelector.close(); } public DequeueValidator getValidator() { if (validator == null) { validator = new DequeueValidator(); } return validator; } public static class DequeueValidator { public boolean validate(String lockKey) { return true; } } private class LockQueuePeriodicTask implements Runnable { @Override public void run() { try { log.info("Distributed lock queue scheduler is running"); Set<String> lockKeys = lockQueue.getLockKeys(); if (lockKeys == null || lockKeys.isEmpty()) { log.info("Lock queue is empty"); return; } log.info("Number of locks to process: {}", lockKeys.size()); for (final String lockKey : lockKeys) { try { log.info("Dequeueing HEAD from lock group: {}", lockKey); DistributedAroundHook aroundHook = coordinator.getDistributedOwnerLockAroundHook(); aroundHook.run(new DistributedAroundHook.Action<Void>() { @Override public Void run() { // Before this method runs, the globalLock will be acquired if (getValidator().validate(lockKey)) { if (!lockQueue.dequeue(lockKey)) { // Nothing was de-queued (empty) so try and remove it lockQueue.removeLockKey(lockKey); } } else { log.info("Skipping as lock is unavailable"); } // After this method runs, the globalLock will be released return null; } }); } catch (Exception e) { log.error("Error occurred whilst processing locks", e); } } } catch (Exception e) { log.error("Unexpected exception", e); } } } }