package com.breakersoft.plow.dispatcher; import java.util.List; import java.util.concurrent.TimeUnit; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; import com.breakersoft.plow.PlowCfg; import com.breakersoft.plow.dispatcher.domain.DispatchJob; import com.breakersoft.plow.dispatcher.domain.DispatchNode; import com.breakersoft.plow.dispatcher.domain.DispatchProc; import com.breakersoft.plow.dispatcher.domain.DispatchProject; import com.breakersoft.plow.dispatcher.domain.DispatchResult; import com.breakersoft.plow.dispatcher.domain.DispatchTask; import com.breakersoft.plow.monitor.PlowStats; import com.breakersoft.plow.rndaemon.RndClientPool; import com.google.common.util.concurrent.RateLimiter; @Component public class NodeDispatcher extends AbstractDispatcher implements Dispatcher<DispatchNode>{ @Autowired private RndClientPool rndClientPool; @Autowired private PlowCfg plowCfg; @Autowired @Qualifier("nodeDispatchExecutor") private ThreadPoolTaskExecutor nodeDispatchExecutor; private RateLimiter rateLimiter; public NodeDispatcher() { } @PostConstruct public void init() { rateLimiter = RateLimiter.create( plowCfg.get("plow.dispatcher.node.procRateLimit", 4.0)); } public void asyncDispatch(final DispatchNode node) { nodeDispatchExecutor.execute(new Runnable() { @Override public void run() { final DispatchResult result = dispatch(node); if (result.procs > 0) { rateLimiter.tryAcquire(result.procs, 5, TimeUnit.SECONDS); PlowStats.nodeDispatchHit.incrementAndGet(); } else { PlowStats.nodeDispatchMiss.incrementAndGet(); } } }); } @Override public DispatchResult dispatch(DispatchNode node) { final DispatchResult result = new DispatchResult(node); dispatch(result, node); return result; } @Override public void dispatch(final DispatchResult result, DispatchNode node) { final List<DispatchProject> projects = dispatchService.getSortedProjectList(node); if (projects.isEmpty()) { return; } for (DispatchProject project: projects) { dispatch(result, node, project); if (!result.continueDispatching()) { return; } } } @Override public void dispatch(final DispatchResult result, DispatchNode node, DispatchProject project) { // Return a list of jobs IDs that have pending frames for the job/node. final List<DispatchJob> jobs = dispatchService.getDispatchJobs(project, node); if (jobs.isEmpty()) { return; } for (DispatchJob job: jobs) { dispatch(result, node, job); if (!result.continueDispatching()) { return; } } } @Override public void dispatch(final DispatchResult result, DispatchNode node, DispatchJob job) { final List<DispatchTask> tasks = dispatchService.getDispatchableTasks(job, node, 15); if (tasks.isEmpty()) { return; } for (DispatchTask task: tasks) { if (!result.canDispatch(task)) { continue; } if (!dispatchService.quotaCheck(node, task)) { result.continueDispatch = false; break; } dispatch(result, node, task); if (!result.continueDispatching()) { break; } } } @Override public void dispatch(final DispatchResult result, DispatchNode node, DispatchTask task) { if (!dispatchService.reserveTask(task)) { return; } DispatchProc proc = null; try { proc = dispatchService.allocateProc(node, task); PlowStats.procAllocCount.incrementAndGet(); } catch (RuntimeException e) { dispatchFailed(result, proc, task, "Unable to allocate proc " + e); PlowStats.nodeDispatchFail.incrementAndGet(); PlowStats.procAllocFailCount.incrementAndGet(); return; } if (dispatchService.startTask(task, proc)) { result.addDispatchPair(proc, task); if (!result.isTest) { rndClientPool.executeProcess(proc, task); } PlowStats.taskStartedCount.incrementAndGet(); } else { dispatchFailed(result, proc, task, "Critical, was able to reserve task but not start it."); PlowStats.nodeDispatchFail.incrementAndGet(); PlowStats.taskStartedFailCount.incrementAndGet(); } } }